TZBTC big_map_diff
for a transfer operation looks like this:
[..., {'action': 'update',
'big_map': '31',
'key_hash': 'expruiaeokjY8rPY52YXKZ6zK7oBN9Cx52psQyHtup13vMUmM7e4X2',
'key': {'bytes': '05070701000000066c65646765720a00000016000016e64994c2ddbd293695b63e4cade029d3c8b5e3'},
'value': {'bytes': '05070700ac9a010200000000'}}, ...]
What we want to get is changed balances with some metadata (for displaying):
{'tz1Mj7RzPmMAqDUNFBn5t5VbXmWW4cSUAdtT': {'balance': 9900,
'decimals': 8,
'symbol': 'TZBTC'}}
parameter (pair %decodeBigMapDiff (bytes %key) (option (bytes %value))) # value can be null, so we made it optional
storage (map (address %holder)
(pair (nat %balance)
(pair (nat %decimals)
(string %symbol))))
BEGIN %decodeBigMapDiff (Pair 0x05070701000000066c65646765720a00000016000016e64994c2ddbd293695b63e4cade029d3c8b5e3
(Some 0x05070700ac9a010200000000))
{} # empty map
value | type |
---|---|
Pair (Pair 0x05070701000000066c65646765720a00000016000016e64994c2ddbd293695b63e4cade029d3c8b5e3 (Some 0x05070700ac9a010200000000)) {} | pair (pair %decodeBigMapDiff (bytes %key) (option (bytes %value))) (map (address %holder) (pair (nat %balance) (pair (nat %decimals) (string %symbol)))) |
DUP ; DIP { CDR @storage } ; # save storage on the bottom of the stack
CAR @parameter
DUP: push ((b'\x05\x07\x07\x01\x00\x00\x00\x06ledger\n\x00\x00\x00\x16\x00\x00\x16\xe6I\x94\xc2\xdd\xbd)6\x95\xb6>L\xad\xe0)\xd3\xc8\xb5\xe3', (b'\x05\x07\x07\x00\xac\x9a\x01\x02\x00\x00\x00\x00',)), {}); DIP: protect 1 item(s); CDR: pop ((b'\x05\x07\x07\x01\x00\x00\x00\x06ledger\n\x00\x00\x00\x16\x00\x00\x16\xe6I\x94\xc2\xdd\xbd)6\x95\xb6>L\xad\xe0)\xd3\xc8\xb5\xe3', (b'\x05\x07\x07\x00\xac\x9a\x01\x02\x00\x00\x00\x00',)), {}); push {}; restore 1 item(s); CAR: pop ((b'\x05\x07\x07\x01\x00\x00\x00\x06ledger\n\x00\x00\x00\x16\x00\x00\x16\xe6I\x94\xc2\xdd\xbd)6\x95\xb6>L\xad\xe0)\xd3\xc8\xb5\xe3', (b'\x05\x07\x07\x00\xac\x9a\x01\x02\x00\x00\x00\x00',)), {}); push (b'\x05\x07\x07\x01\x00\x00\x00\x06ledger\n\x00\x00\x00\x16\x00\x00\x16\xe6I\x94\xc2\xdd\xbd)6\x95\xb6>L\xad\xe0)\xd3\xc8\xb5\xe3', (b'\x05\x07\x07\x00\xac\x9a\x01\x02\x00\x00\x00\x00',));
value | type | name |
---|---|---|
Pair 0x05070701000000066c65646765720a00000016000016e64994c2ddbd293695b63e4cade029d3c8b5e3 (Some 0x05070700ac9a010200000000) | pair %decodeBigMapDiff (bytes %key) (option (bytes %value)) | @parameter |
DUP ; CAR @key # first let's check the key
DUP: push (b'\x05\x07\x07\x01\x00\x00\x00\x06ledger\n\x00\x00\x00\x16\x00\x00\x16\xe6I\x94\xc2\xdd\xbd)6\x95\xb6>L\xad\xe0)\xd3\xc8\xb5\xe3', (b'\x05\x07\x07\x00\xac\x9a\x01\x02\x00\x00\x00\x00',)); CAR: pop (b'\x05\x07\x07\x01\x00\x00\x00\x06ledger\n\x00\x00\x00\x16\x00\x00\x16\xe6I\x94\xc2\xdd\xbd)6\x95\xb6>L\xad\xe0)\xd3\xc8\xb5\xe3', (b'\x05\x07\x07\x00\xac\x9a\x01\x02\x00\x00\x00\x00',)); push 05070701000000066c65646765720a00000016000016e64994c2ddbd293695b63e4cade029d3c8b5e3;
value | type | name |
---|---|---|
0x05070701000000066c65646765720a00000016000016e64994c2ddbd293695b63e4cade029d3c8b5e3 | bytes %key | @key |
UNPACK (pair string address) ; # trying to unpack big map key (they can be of different types underneath)
ASSERT_SOME
UNPACK: pop 05070701000000066c65646765720a00000016000016e64994c2ddbd293695b63e4cade029d3c8b5e3; push (('ledger', 'tz1Mj7RzPmMAqDUNFBn5t5VbXmWW4cSUAdtT'),); IF_NONE: pop (('ledger', 'tz1Mj7RzPmMAqDUNFBn5t5VbXmWW4cSUAdtT'),); push ('ledger', 'tz1Mj7RzPmMAqDUNFBn5t5VbXmWW4cSUAdtT'); RENAME: pop ('ledger', 'tz1Mj7RzPmMAqDUNFBn5t5VbXmWW4cSUAdtT'); push ('ledger', 'tz1Mj7RzPmMAqDUNFBn5t5VbXmWW4cSUAdtT');
value | type |
---|---|
Pair "ledger" 0x000016e64994c2ddbd293695b63e4cade029d3c8b5e3 | pair string address |
DUP ; CAR @label
DUP: push ('ledger', 'tz1Mj7RzPmMAqDUNFBn5t5VbXmWW4cSUAdtT'); CAR: pop ('ledger', 'tz1Mj7RzPmMAqDUNFBn5t5VbXmWW4cSUAdtT'); push ledger;
value | type | name |
---|---|---|
"ledger" | string | @label |
PUSH string "ledger" ; ASSERT_CMPEQ ; # make sure it's balance data
PUSH: push ledger; COMPARE: pop ledger, ledger; push 0; EQ: pop 0; push True; IF: pop True;
DUMP
value | type | name |
---|---|---|
Pair "ledger" 0x000016e64994c2ddbd293695b63e4cade029d3c8b5e3 | pair string address | |
Pair 0x05070701000000066c65646765720a00000016000016e64994c2ddbd293695b63e4cade029d3c8b5e3 (Some 0x05070700ac9a010200000000) | pair %decodeBigMapDiff (bytes %key) (option (bytes %value)) | @parameter |
{} | map (address %holder) (pair (nat %balance) (pair (nat %decimals) (string %symbol))) | @storage |
CDR @holder ;
value | type | name |
---|---|---|
0x000016e64994c2ddbd293695b63e4cade029d3c8b5e3 | address | @holder |
SWAP ; CDR @value ;
ASSERT_SOME # actually we need to handle cases when a key is removed from big_map, will do later
SWAP: pop tz1Mj7RzPmMAqDUNFBn5t5VbXmWW4cSUAdtT, (b'\x05\x07\x07\x01\x00\x00\x00\x06ledger\n\x00\x00\x00\x16\x00\x00\x16\xe6I\x94\xc2\xdd\xbd)6\x95\xb6>L\xad\xe0)\xd3\xc8\xb5\xe3', (b'\x05\x07\x07\x00\xac\x9a\x01\x02\x00\x00\x00\x00',)); push tz1Mj7RzPmMAqDUNFBn5t5VbXmWW4cSUAdtT; push (b'\x05\x07\x07\x01\x00\x00\x00\x06ledger\n\x00\x00\x00\x16\x00\x00\x16\xe6I\x94\xc2\xdd\xbd)6\x95\xb6>L\xad\xe0)\xd3\xc8\xb5\xe3', (b'\x05\x07\x07\x00\xac\x9a\x01\x02\x00\x00\x00\x00',)); CDR: pop (b'\x05\x07\x07\x01\x00\x00\x00\x06ledger\n\x00\x00\x00\x16\x00\x00\x16\xe6I\x94\xc2\xdd\xbd)6\x95\xb6>L\xad\xe0)\xd3\xc8\xb5\xe3', (b'\x05\x07\x07\x00\xac\x9a\x01\x02\x00\x00\x00\x00',)); push (b'\x05\x07\x07\x00\xac\x9a\x01\x02\x00\x00\x00\x00',); IF_NONE: pop (b'\x05\x07\x07\x00\xac\x9a\x01\x02\x00\x00\x00\x00',); push 05070700ac9a010200000000; RENAME: pop 05070700ac9a010200000000; push 05070700ac9a010200000000;
value | type |
---|---|
0x05070700ac9a010200000000 | bytes %value |
UNPACK (pair nat (map address nat)) ;
ASSERT_SOME # here we can fail, since we know exactly how data is packed
UNPACK: pop 05070700ac9a010200000000; push ((9900, {}),); IF_NONE: pop ((9900, {}),); push (9900, {}); RENAME: pop (9900, {}); push (9900, {});
value | type |
---|---|
Pair 9900 {} | pair nat (map address nat) |
CAR @balance
value | type | name |
---|---|---|
9900 | nat | @balance |
PUSH @symbol string "TZBTC" ; # adding metadata
PUSH @decimals nat 8 ;
PUSH: push TZBTC; PUSH: push 8;
value | type | name |
---|---|---|
8 | nat | @decimals |
DUMP
value | type | name |
---|---|---|
8 | nat | @decimals |
"TZBTC" | string | @symbol |
9900 | nat | @balance |
0x000016e64994c2ddbd293695b63e4cade029d3c8b5e3 | address | |
{} | map (address %holder) (pair (nat %balance) (pair (nat %decimals) (string %symbol))) | @storage |
PAIR ; SWAP ; PAIR ; SOME ;
PAIR: pop 8, TZBTC; push (8, 'TZBTC'); SWAP: pop (8, 'TZBTC'), 9900; push (8, 'TZBTC'); push 9900; PAIR: pop 9900, (8, 'TZBTC'); push (9900, (8, 'TZBTC')); SOME: pop (9900, (8, 'TZBTC')); push ((9900, (8, 'TZBTC')),);
value | type |
---|---|
Some (Pair 9900 (Pair 8 "TZBTC")) | option (pair nat (pair nat string)) |
SWAP ; UPDATE # writing to the storage
SWAP: pop ((9900, (8, 'TZBTC')),), tz1Mj7RzPmMAqDUNFBn5t5VbXmWW4cSUAdtT; push ((9900, (8, 'TZBTC')),); push tz1Mj7RzPmMAqDUNFBn5t5VbXmWW4cSUAdtT; UPDATE: pop tz1Mj7RzPmMAqDUNFBn5t5VbXmWW4cSUAdtT, ((9900, (8, 'TZBTC')),), {}; push {'tz1Mj7RzPmMAqDUNFBn5t5VbXmWW4cSUAdtT': (9900, (8, 'TZBTC'))};
value | type |
---|---|
{ Elt 0x000016e64994c2ddbd293695b63e4cade029d3c8b5e3 (Pair 9900 (Pair 8 "TZBTC")) } | map (address %holder) (pair (nat %balance) (pair (nat %decimals) (string %symbol))) |
NIL operation ; PAIR ; COMMIT
NIL: push []; PAIR: pop [], {'tz1Mj7RzPmMAqDUNFBn5t5VbXmWW4cSUAdtT': (9900, (8, 'TZBTC'))}; push ([], {'tz1Mj7RzPmMAqDUNFBn5t5VbXmWW4cSUAdtT': (9900, (8, 'TZBTC'))}); COMMIT:
value | type |
---|---|
{ Elt 0x000016e64994c2ddbd293695b63e4cade029d3c8b5e3 (Pair 9900 (Pair 8 "TZBTC")) } | map (address %holder) (pair (nat %balance) (pair (nat %decimals) (string %symbol))) |
Actually a valid Tezos contract
parameter (pair %decodeBigMapDiff (bytes %key) (option (bytes %value))) ;
storage (map (address %holder)
(pair (nat %balance)
(pair (nat %decimals)
(string %symbol)))) ;
code {
DUP ; CAR ; DIP { CDR } ;
DUP ; CAR ;
UNPACK (pair string address) ;
IF_SOME {
DUP ; CAR ;
PUSH string "ledger" ;
IFCMPEQ {
CDR @holder ;
SWAP ; CDR ;
IF_SOME {
UNPACK (pair nat (map address nat)) ;
ASSERT_SOME ;
CAR @balance ;
} {
PUSH @balance nat 0;
} ;
PUSH @symbol string "TZBTC" ;
PUSH @decimals nat 8 ;
PAIR ; SWAP ; PAIR ; SOME ;
SWAP ; UPDATE
} {
DROP 2
} ;
} {
DROP
} ;
NIL operation ; PAIR ;
}
parameter (pair %decodeBigMapDiff (bytes %key) (option (bytes %value))); storage (map (address %holder) (pair (nat %balance) (pair (nat %decimals) (string %symbol)))); code { DUP ; CAR ; DIP { CDR } ; DUP ; CAR ; UNPACK (pair string address) ; { IF_NONE { DROP } { DUP ; CAR ; PUSH string "ledger" ; { { COMPARE ; EQ } ; IF { CDR @holder ; SWAP ; CDR ; { IF_NONE { PUSH @balance nat 0 } { UNPACK (pair nat (map address nat)) ; { IF_NONE { { UNIT ; FAILWITH } } { RENAME } } ; CAR @balance } } ; PUSH @symbol string "TZBTC" ; PUSH @decimals nat 8 ; PAIR ; SWAP ; PAIR ; SOME ; SWAP ; UPDATE } { DROP 2 } } } } ; NIL operation ; PAIR };
Now let's run some tests
DEBUG False
RUN %decodeBigMapDiff (Pair 0x05070701000000066c65646765720a00000016000016e64994c2ddbd293695b63e4cade029d3c8b5e3
(Some 0x05070700ac9a010200000000))
{}
value | type |
---|---|
{ Elt 0x000016e64994c2ddbd293695b63e4cade029d3c8b5e3 (Pair 9900 (Pair 8 "TZBTC")) } | map (address %holder) (pair (nat %balance) (pair (nat %decimals) (string %symbol))) |
RUN %decodeBigMapDiff (Pair 0x05070701000000066c65646765720a00000016000016e64994c2ddbd293695b63e4cade029d3c8b5e3
None)
{}
value | type |
---|---|
{ Elt 0x000016e64994c2ddbd293695b63e4cade029d3c8b5e3 (Pair 0 (Pair 8 "TZBTC")) } | map (address %holder) (pair (nat %balance) (pair (nat %decimals) (string %symbol))) |
RUN %decodeBigMapDiff (Pair 0xdeadbeef None) {}
value | type |
---|---|
{} | map (address %holder) (pair (nat %balance) (pair (nat %decimals) (string %symbol))) |