Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions synapse/models/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ def getModelDefs(self):
('crypto:currency:transaction', ('guid', {}), {
'doc': 'An individual crypto currency transaction recorded on the blockchain.',
}),
('crypto:currency:bridge:swap', ('guid', {}), {
'doc': 'A cross-chain swap which bridges value between two transactions on different chains.',
}),
('crypto:currency:block', ('comp', {'fields': (
('coin', 'crypto:currency:coin'),
('offset', 'int'),
Expand Down Expand Up @@ -86,6 +89,18 @@ def getModelDefs(self):
'doc': 'A smart contract effect which grants a non-owner address the ability to manipulate fungible tokens.',
'interfaces': ('crypto:smart:effect',),
}),
('crypto:smart:effect:freeze', ('guid', {}), {
'doc': 'A smart contract effect which freezes or unfreezes an address on an issuer blocklist.',
'interfaces': ('crypto:smart:effect',),
}),
('crypto:smart:effect:seize', ('guid', {}), {
'doc': 'A smart contract effect which destroys the tokens held by an address.',
'interfaces': ('crypto:smart:effect',),
}),
('crypto:smart:effect:swaptokens', ('guid', {}), {
'doc': 'A smart contract effect which swaps one token or currency for another.',
'interfaces': ('crypto:smart:effect',),
}),
# TODO crypto:smart:effect:call - call another smart contract
# TODO crypto:smart:effect:giveproxy - grant your proxy for a token based vote
('crypto:payment:input', ('guid', {}), {
Expand All @@ -94,6 +109,9 @@ def getModelDefs(self):
('crypto:payment:output', ('guid', {}), {
'doc': 'A payment received from a transaction.',
}),
('crypto:payment:fee', ('guid', {}), {
'doc': 'A fee paid to execute a transaction.',
}),
('crypto:smart:token', ('comp', {'fields': (('contract', 'crypto:smart:contract'), ('tokenid', 'hugenum'))}), {
'doc': 'A token managed by a smart contract.',
}),
Expand Down Expand Up @@ -222,6 +240,14 @@ def getModelDefs(self):
('value', ('econ:price', {}), {
'doc': 'The value of the currency received from the transaction.'}),
)),
('crypto:payment:fee', {}, (
('transaction', ('crypto:currency:transaction', {}), {
'doc': 'The transaction the fee was paid for.'}),
('address', ('crypto:currency:address', {}), {
'doc': 'The address which paid the fee.'}),
('value', ('econ:price', {}), {
'doc': 'The value of the fee paid to execute the transaction.'}),
)),
('crypto:currency:transaction', {}, (
('hash', ('hex', {}), {
'doc': 'The unique transaction hash for the transaction.'}),
Expand Down Expand Up @@ -274,6 +300,29 @@ def getModelDefs(self):
# TODO break out args/retvals and maybe make humon repr?
)),

('crypto:currency:bridge:swap', {}, (
('name', ('str', {'strip': True}), {
'doc': 'The name of the bridge protocol which executed the swap.'}),
('matched', ('bool', {}), {
'doc': 'Set to true if the two transactions were matched together by an external observer.'}),
('origin:transaction', ('crypto:currency:transaction', {}), {
'doc': 'The transaction which sent value on the origin chain.'}),
('origin:address', ('crypto:currency:address', {}), {
'doc': 'The address which sent value on the origin chain.'}),
('origin:contract', ('crypto:smart:contract', {}), {
'doc': 'The contract which defines the tokens sent on the origin chain. This is not set if the sent value was a native currency.'}),
('origin:amount', ('hugenum', {}), {
'doc': 'The amount of tokens or currency sent on the origin chain.'}),
('destination:transaction', ('crypto:currency:transaction', {}), {
'doc': 'The transaction which received value on the destination chain.'}),
('destination:address', ('crypto:currency:address', {}), {
'doc': 'The address which received value on the destination chain.'}),
('destination:contract', ('crypto:smart:contract', {}), {
'doc': 'The contract which defines the tokens received on the destination chain. This is not set if the received value was a native currency.'}),
('destination:amount', ('hugenum', {}), {
'doc': 'The amount of tokens or currency received on the destination chain.'}),
)),

('crypto:currency:block', {}, (
('coin', ('crypto:currency:coin', {}), {
'doc': 'The coin/blockchain this block resides on.', 'ro': True, }),
Expand Down Expand Up @@ -398,6 +447,39 @@ def getModelDefs(self):
'doc': 'The hex encoded amount of tokens the proxy is allowed to manipulate.'}),
)),

('crypto:smart:effect:freeze', {}, (
('contract', ('crypto:smart:contract', {}), {
'doc': 'The contract which enforces the freeze.'}),
('address', ('crypto:currency:address', {}), {
'doc': 'The address which was frozen or unfrozen.'}),
('frozen', ('bool', {}), {
'doc': 'Set to true if the address was frozen or false if it was unfrozen.'}),
)),

('crypto:smart:effect:seize', {}, (
('contract', ('crypto:smart:contract', {}), {
'doc': 'The contract which defines the tokens.'}),
('address', ('crypto:currency:address', {}), {
'doc': 'The address whose tokens were destroyed.'}),
('amount', ('hugenum', {}), {
'doc': 'The amount of tokens destroyed.'}),
)),

('crypto:smart:effect:swaptokens', {}, (
('contract', ('crypto:smart:contract', {}), {
'doc': 'The pool or router contract which executed the swap.'}),
('address', ('crypto:currency:address', {}), {
'doc': 'The address which swapped the tokens.'}),
('sent:contract', ('crypto:smart:contract', {}), {
'doc': 'The contract which defines the sent tokens. This is not set if the sent value was a native currency.'}),
('sent:amount', ('hugenum', {}), {
'doc': 'The amount of tokens or currency sent by the address.'}),
('received:contract', ('crypto:smart:contract', {}), {
'doc': 'The contract which defines the received tokens. This is not set if the received value was a native currency.'}),
('received:amount', ('hugenum', {}), {
'doc': 'The amount of tokens or currency received by the address.'}),
)),

('crypto:currency:address', {}, (
('coin', ('crypto:currency:coin', {}), {
'doc': 'The crypto coin to which the address belongs.', 'ro': True, }),
Expand Down
120 changes: 120 additions & 0 deletions synapse/tests/test_model_crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,13 @@ async def test_model_crypto_currency(self):
self.eq(payees[0].get('index'), 0)
self.eq(payees[0].get('address'), ('btc', '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2'))

payfees = await core.nodes('[ crypto:payment:fee=(t1, feepayer) :transaction=(t1,) :address=(btc, 1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2) :value=0.0001 ]')
self.len(1, payfees)
self.eq(payfees[0].get('value'), '0.0001')
self.eq(payfees[0].get('address'), ('btc', '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2'))
self.len(1, await core.nodes('crypto:payment:fee -> crypto:currency:transaction'))
self.len(1, await core.nodes('crypto:payment:fee -> crypto:currency:address'))

payor = payors[0].ndef[1]
payee = payees[0].ndef[1]

Expand Down Expand Up @@ -355,6 +362,119 @@ async def test_model_crypto_currency(self):
self.len(1, await core.nodes('crypto:smart:effect:proxytokens -> crypto:currency:transaction'))
self.len(1, await core.nodes('crypto:smart:effect:proxytokens -> crypto:smart:contract'))

nodes = await core.nodes('''
[
crypto:smart:effect:freeze=*
:index=0
:transaction=*
:contract=*
:address=(eth, 0xd73d44149deb8aabd4b53197654a286dedfda827)
:frozen=$lib.true
]''')
self.len(1, nodes)
node = nodes[0]
self.eq(node.get('index'), 0)
self.nn(node.get('contract'))
self.eq(node.get('address'), ('eth', '0xd73d44149deb8aabd4b53197654a286dedfda827'))
self.true(node.get('frozen'))
self.len(1, await core.nodes('crypto:smart:effect:freeze -> crypto:currency:address'))
self.len(1, await core.nodes('crypto:smart:effect:freeze -> crypto:currency:transaction'))
self.len(1, await core.nodes('crypto:smart:effect:freeze -> crypto:smart:contract'))

nodes = await core.nodes('''
[
crypto:smart:effect:seize=*
:index=0
:transaction=*
:contract=*
:address=(eth, 0xd73d44149deb8aabd4b53197654a286dedfda827)
:amount=960007.90
]''')
self.len(1, nodes)
node = nodes[0]
self.eq(node.get('index'), 0)
self.nn(node.get('contract'))
self.eq(node.get('address'), ('eth', '0xd73d44149deb8aabd4b53197654a286dedfda827'))
self.eq(node.get('amount'), '960007.9')
self.len(1, await core.nodes('crypto:smart:effect:seize -> crypto:currency:address'))
self.len(1, await core.nodes('crypto:smart:effect:seize -> crypto:currency:transaction'))
self.len(1, await core.nodes('crypto:smart:effect:seize -> crypto:smart:contract'))

nodes = await core.nodes('''
[
crypto:smart:effect:swaptokens=*
:index=179
:transaction=*
:contract=*
:address=(eth, 0x241ebd14e40899ddb1191c140f85c37917b0505b)
:sent:contract=*
:sent:amount=0.025194371619325567
:received:contract=*
:received:amount=22574.721
]''')
self.len(1, nodes)
node = nodes[0]
self.eq(node.get('index'), 179)
self.nn(node.get('contract'))
self.eq(node.get('address'), ('eth', '0x241ebd14e40899ddb1191c140f85c37917b0505b'))
self.nn(node.get('sent:contract'))
self.eq(node.get('sent:amount'), '0.025194371619325567')
self.nn(node.get('received:contract'))
self.eq(node.get('received:amount'), '22574.721')
self.len(1, await core.nodes('crypto:smart:effect:swaptokens -> crypto:currency:address'))
self.len(1, await core.nodes('crypto:smart:effect:swaptokens -> crypto:currency:transaction'))
self.len(3, await core.nodes('crypto:smart:effect:swaptokens -> crypto:smart:contract'))

nodes = await core.nodes('''
[
crypto:currency:bridge:swap=(bridge1,)
:name="deBridge.finance"
:matched=true
:origin:transaction=(otxn,)
:origin:address=(eth, 0xaaaa)
:origin:contract=*
:origin:amount=1.5
:destination:transaction=(dtxn,)
:destination:address=(bsc, 0xbbbb)
:destination:contract=*
:destination:amount=1490.0
]''')
self.len(1, nodes)
node = nodes[0]
self.eq(node.get('name'), 'deBridge.finance')
self.eq(node.get('matched'), True)
self.eq(node.get('origin:address'), ('eth', '0xaaaa'))
self.nn(node.get('origin:contract'))
self.eq(node.get('origin:amount'), '1.5')
self.eq(node.get('destination:address'), ('bsc', '0xbbbb'))
self.nn(node.get('destination:contract'))
self.eq(node.get('destination:amount'), '1490')

# the swap is reachable from either leg via its indexed transaction prop, and bridges to the other leg
self.len(1, await core.nodes('crypto:currency:bridge:swap:origin:transaction=(otxn,)'))
self.len(1, await core.nodes('crypto:currency:bridge:swap:destination:transaction=(dtxn,)'))
self.len(2, await core.nodes('crypto:currency:bridge:swap=(bridge1,) -> crypto:currency:transaction'))
nodes = await core.nodes('crypto:currency:bridge:swap:origin:transaction=(otxn,) :destination:transaction -> crypto:currency:transaction')
self.len(1, nodes)
self.eq(nodes[0].ndef, ('crypto:currency:transaction', s_common.guid(('dtxn',))))

# is:matched does not imply the destination leg is populated
nodes = await core.nodes('''
[
crypto:currency:bridge:swap=(bridge2,)
:matched=true
:origin:transaction=(otxn2,)
:origin:address=(eth, 0xcccc)
:origin:amount=2.0
:destination:transaction=(dtxn2,)
]''')
self.len(1, nodes)
node = nodes[0]
self.nn(node.get('destination:transaction'))
self.none(node.get('destination:address'))
self.none(node.get('destination:contract'))
self.none(node.get('destination:amount'))

nodes = await core.nodes('''
[
crypto:smart:token=(2bdea834252a220b61aadf592cc0de66, 30)
Expand Down
Loading