-
Notifications
You must be signed in to change notification settings - Fork 39
/
Tzip7ReferenceTokenHelper.ts
164 lines (120 loc) · 17.9 KB
/
Tzip7ReferenceTokenHelper.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import { JSONPath } from 'jsonpath-plus';
import { KeyStore, Signer } from '../../../types/ExternalInterfaces';
import * as TezosTypes from '../../../types/tezos/TezosChainTypes';
import { TezosConstants } from '../../../types/tezos/TezosConstants';
import { TezosMessageUtils } from '../TezosMessageUtil';
import { TezosNodeReader } from '../TezosNodeReader';
import { TezosNodeWriter } from '../TezosNodeWriter';
import { TezosContractUtils } from './TezosContractUtils';
import { Transaction } from '../../../types/tezos/TezosP2PMessageTypes';
/**
* Interface for the FA1.2 contract implementation from the Morley Project outlined here: https://gitlab.com/tzip/tzip/blob/master/proposals/tzip-7/ManagedLedger.md
*
* Compatible with the contract as of January 22, 2020 from https://gitlab.com/tzip/tzip/raw/master/proposals/tzip-7/ManagedLedger.tz
*
* This wrapper does not include support for the following contract functions: getAllowance, getBalance, getTotalSupply, getAdministrator. This information is retrieved by querying the big_map structure on chain directly.
*/
export namespace Tzip7ReferenceTokenHelper {
/**
* Gets the contract code at the specified address at the head block and compares it to the known hash of the code. This function processes Micheline format contracts.
*
*
* @param server Destination Tezos node.
* @param address Contract address to query.
*/
export async function verifyDestination(server: string, address: string): Promise<boolean> {
return TezosContractUtils.verifyDestination(server, address, '0e3e137841a959521324b4ce20ca2df7');
}
/**
* In contrast to verifyDestination, this function uses compares Michelson hashes.
*
* @param script
*/
export function verifyScript(script: string): boolean {
return TezosContractUtils.verifyScript(script, 'b77ada691b1d630622bea243696c84d7');
}
export async function deployContract(server: string, signer: Signer, keystore: KeyStore, fee: number, administrator: string, pause: boolean = true, supply: number = 0, gas: number = 150_000, freight: number = 5_000) {
const contract = `parameter (or (or (or (pair %transfer (address :from) (pair (address :to) (nat :value))) (pair %approve (address :spender) (nat :value))) (or (pair %getAllowance (pair (address :owner) (address :spender)) (contract nat)) (or (pair %getBalance (address :owner) (contract nat)) (pair %getTotalSupply unit (contract nat))))) (or (or (bool %setPause) (address %setAdministrator)) (or (pair %getAdministrator unit (contract address)) (or (pair %mint (address :to) (nat :value)) (pair %burn (address :from) (nat :value))))));
storage (pair (big_map %ledger (address :user) (pair (nat :balance) (map :approvals (address :spender) (nat :value)))) (pair (address %admin) (pair (bool %paused) (nat %totalSupply))));
code { CAST (pair (or (or (or (pair address (pair address nat)) (pair address nat)) (or (pair (pair address address) (contract nat)) (or (pair address (contract nat)) (pair unit (contract nat))))) (or (or bool address) (or (pair unit (contract address)) (or (pair address nat) (pair address nat))))) (pair (big_map address (pair nat (map address nat))) (pair address (pair bool nat)))); DUP; CAR; DIP { CDR }; IF_LEFT { IF_LEFT { IF_LEFT { DIP { DUP; CDR; CDR; CAR; IF { UNIT; PUSH string "TokenOperationsArePaused"; PAIR; FAILWITH } { } }; DUP; DUP; CDR; CAR; DIP { CAR }; COMPARE; EQ; IF { DROP } { DUP; CAR; SENDER; COMPARE; EQ; IF { } { DUP; DIP { DUP; DIP { DIP { DUP }; CAR; SENDER; PAIR; DUP; DIP { CDR; DIP { CAR }; GET; IF_NONE { EMPTY_MAP (address) nat } { CDR } }; CAR; GET; IF_NONE { PUSH nat 0 } { } }; DUP; CAR; DIP { SENDER; DIP { DUP; CDR; CDR; DIP { DIP { DUP }; SWAP }; SWAP; SUB; ISNAT; IF_NONE { DIP { DUP }; SWAP; DIP { DUP }; SWAP; CDR; CDR; PAIR; PUSH string "NotEnoughAllowance"; PAIR; FAILWITH } { } }; PAIR }; PAIR; DIP { DROP; DROP }; DIP { DUP }; SWAP; DIP { DUP; CAR }; SWAP; DIP { CAR }; GET; IF_NONE { PUSH nat 0; DIP { EMPTY_MAP (address) nat }; PAIR; EMPTY_MAP (address) nat } { DUP; CDR }; DIP { DIP { DUP }; SWAP }; SWAP; CDR; CDR; DUP; INT; EQ; IF { DROP; NONE nat } { SOME }; DIP { DIP { DIP { DUP }; SWAP }; SWAP }; SWAP; CDR; CAR; UPDATE; DIP { DUP; DIP { CAR }; CDR }; DIP { DROP }; SWAP; PAIR; SWAP; CAR; DIP { SOME }; DIP { DIP { DUP; CAR } }; UPDATE; DIP { DUP; DIP { CDR }; CAR }; DIP { DROP }; PAIR } }; DIP { DUP }; SWAP; DIP { DUP }; SWAP; CDR; CAR; DIP { CAR }; GET; IF_NONE { DUP; CDR; CDR; INT; EQ; IF { NONE (pair nat (map address nat)) } { DUP; CDR; CDR; DIP { EMPTY_MAP (address) nat }; PAIR; SOME } } { DIP { DUP }; SWAP; CDR; CDR; DIP { DUP; CAR }; ADD; DIP { DUP; DIP { CDR }; CAR }; DIP { DROP }; PAIR; SOME }; SWAP; DUP; DIP { CDR; CAR; DIP { DIP { DUP; CAR } }; UPDATE; DIP { DUP; DIP { CDR }; CAR }; DIP { DROP }; PAIR }; DUP; DIP { CDR; CDR; INT; DIP { DUP; CDR; CDR; CDR }; ADD; ISNAT; IF_NONE { PUSH string "Internal: Negative total supply"; FAILWITH } { }; DIP { DUP; CDR }; DIP { DUP; DIP { CAR }; CDR }; DIP { DUP; DIP { CAR }; CDR }; DIP { DROP }; SWAP; PAIR; SWAP; PAIR; DIP { DUP; DIP { CAR }; CDR }; DIP { DROP }; SWAP; PAIR }; DIP { DUP }; SWAP; DIP { DUP }; SWAP; CAR; DIP { CAR }; GET; IF_NONE { CDR; CDR; PUSH nat 0; SWAP; PAIR; PUSH string "NotEnoughBalance"; PAIR; FAILWITH } { }; DUP; CAR; DIP { DIP { DUP }; SWAP }; SWAP; CDR; CDR; SWAP; SUB; ISNAT; IF_NONE { CAR; DIP { DUP }; SWAP; CDR; CDR; PAIR; PUSH string "NotEnoughBalance"; PAIR; FAILWITH } { }; DIP { DUP; DIP { CDR }; CAR }; DIP { DROP }; PAIR; DIP { DUP }; SWAP; DIP { DUP; CAR; INT; EQ; IF { DUP; CDR; SIZE; INT; EQ; IF { DROP; NONE (pair nat (map address nat)) } { SOME } } { SOME }; SWAP; CAR; DIP { DIP { DUP; CAR } }; UPDATE; DIP { DUP; DIP { CDR }; CAR }; DIP { DROP }; PAIR }; DUP; DIP { CDR; CDR; NEG; DIP { DUP; CDR; CDR; CDR }; ADD; ISNAT; IF_NONE { PUSH string "Internal: Negative total supply"; FAILWITH } { }; DIP { DUP; CDR }; DIP { DUP; DIP { CAR }; CDR }; DIP { DUP; DIP { CAR }; CDR }; DIP { DROP }; SWAP; PAIR; SWAP; PAIR; DIP { DUP; DIP { CAR }; CDR }; DIP { DROP }; SWAP; PAIR }; DROP }; NIL operation; PAIR } { SENDER; PAIR; DIP { DUP; CDR; CDR; CAR; IF { UNIT; PUSH string "TokenOperationsArePaused"; PAIR; FAILWITH } { } }; DIP { DUP }; SWAP; DIP { DUP }; SWAP; DUP; DIP { CAR; DIP { CAR }; GET; IF_NONE { EMPTY_MAP (address) nat } { CDR } }; CDR; CAR; GET; IF_NONE { PUSH nat 0 } { }; DUP; INT; EQ; IF { DROP } { DIP { DUP }; SWAP; CDR; CDR; INT; EQ; IF { DROP } { PUSH string "UnsafeAllowanceChange"; PAIR; FAILWITH } }; DIP { DUP }; SWAP; DIP { DUP; CAR }; SWAP; DIP { CAR }; GET; IF_NONE { PUSH nat 0; DIP { EMPTY_MAP (address) nat }; PAIR; EMPTY_MAP (address) nat } { DUP; CDR }; DIP { DIP { DUP }; SWAP }; SWAP; CDR; CDR; DUP; INT; EQ; IF { DROP; NONE nat } { SOME }; DIP { DIP { DIP { DUP }; SWAP }; SWAP }; SWAP; CDR; CAR; UPDATE; DIP { DUP; DIP { CAR }; CDR }; DIP { DROP }; SWAP; PAIR; SWAP; CAR; DIP { SOME }; DIP { DIP { DUP; CAR } }; UPDATE; DIP { DUP; DIP { CDR }; CAR }; DIP { DROP }; PAIR; NIL operation; PAIR } } { IF_LEFT { DUP; CAR; DIP { CDR }; DIP { DIP { DUP }; SWAP }; PAIR; DUP; CAR; DIP { CDR }; DUP; DIP { CAR; DIP { CAR }; GET; IF_NONE { EMPTY_MAP (address) nat } { CDR } }; CDR; GET; IF_NONE { PUSH nat 0 } { }; DIP { AMOUNT }; TRANSFER_TOKENS; NIL operation; SWAP; CONS; PAIR } { IF_LEFT { DUP; CAR; DIP { CDR }; DIP { DIP { DUP }; SWAP }; PAIR; DUP; CAR; DIP { CDR }; DIP { CAR }; GET; IF_NONE { PUSH nat 0 } { CAR }; DIP { AMOUNT }; TRANSFER_TOKENS; NIL operation; SWAP; CONS; PAIR } { DUP; CAR; DIP { CDR }; DIP { DIP { DUP }; SWAP }; PAIR; CDR; CDR; CDR; CDR; DIP { AMOUNT }; TRANSFER_TOKENS; NIL operation; SWAP; CONS; PAIR } } } } { IF_LEFT { IF_LEFT { DIP { DUP; CDR; CAR; SENDER; COMPARE; EQ; IF { } { UNIT; PUSH string "SenderIsNotAdmin"; PAIR; FAILWITH } }; DIP { DUP; CDR }; DIP { DUP; DIP { CAR }; CDR }; DIP { DUP; DIP { CDR }; CAR }; DIP { DROP }; PAIR; SWAP; PAIR; DIP { DUP; DIP { CAR }; CDR }; DIP { DROP }; SWAP; PAIR; NIL operation; PAIR } { DIP { DUP; CDR; CAR; SENDER; COMPARE; EQ; IF { } { UNIT; PUSH string "SenderIsNotAdmin"; PAIR; FAILWITH } }; DIP { DUP; CDR }; DIP { DUP; DIP { CDR }; CAR }; DIP { DROP }; PAIR; DIP { DUP; DIP { CAR }; CDR }; DIP { DROP }; SWAP; PAIR; NIL operation; PAIR } } { IF_LEFT { DUP; CAR; DIP { CDR }; DIP { DIP { DUP }; SWAP }; PAIR; CDR; CDR; CAR; DIP { AMOUNT }; TRANSFER_TOKENS; NIL operation; SWAP; CONS; PAIR } { IF_LEFT { DIP { DUP; CDR; CAR; SENDER; COMPARE; EQ; IF { } { UNIT; PUSH string "SenderIsNotAdmin"; PAIR; FAILWITH } }; DIP { DUP }; SWAP; DIP { DUP }; SWAP; CAR; DIP { CAR }; GET; IF_NONE { DUP; CDR; INT; EQ; IF { NONE (pair nat (map address nat)) } { DUP; CDR; DIP { EMPTY_MAP (address) nat }; PAIR; SOME } } { DIP { DUP }; SWAP; CDR; DIP { DUP; CAR }; ADD; DIP { DUP; DIP { CDR }; CAR }; DIP { DROP }; PAIR; SOME }; SWAP; DUP; DIP { CAR; DIP { DIP { DUP; CAR } }; UPDATE; DIP { DUP; DIP { CDR }; CAR }; DIP { DROP }; PAIR }; DUP; DIP { CDR; INT; DIP { DUP; CDR; CDR; CDR }; ADD; ISNAT; IF_NONE { PUSH string "Internal: Negative total supply"; FAILWITH } { }; DIP { DUP; CDR }; DIP { DUP; DIP { CAR }; CDR }; DIP { DUP; DIP { CAR }; CDR }; DIP { DROP }; SWAP; PAIR; SWAP; PAIR; DIP { DUP; DIP { CAR }; CDR }; DIP { DROP }; SWAP; PAIR }; DROP; NIL operation; PAIR } { DIP { DUP; CDR; CAR; SENDER; COMPARE; EQ; IF { } { UNIT; PUSH string "SenderIsNotAdmin"; PAIR; FAILWITH } }; DIP { DUP }; SWAP; DIP { DUP }; SWAP; CAR; DIP { CAR }; GET; IF_NONE { CDR; PUSH nat 0; SWAP; PAIR; PUSH string "NotEnoughBalance"; PAIR; FAILWITH } { }; DUP; CAR; DIP { DIP { DUP }; SWAP }; SWAP; CDR; SWAP; SUB; ISNAT; IF_NONE { CAR; DIP { DUP }; SWAP; CDR; PAIR; PUSH string "NotEnoughBalance"; PAIR; FAILWITH } { }; DIP { DUP; DIP { CDR }; CAR }; DIP { DROP }; PAIR; DIP { DUP }; SWAP; DIP { DUP; CAR; INT; EQ; IF { DUP; CDR; SIZE; INT; EQ; IF { DROP; NONE (pair nat (map address nat)) } { SOME } } { SOME }; SWAP; CAR; DIP { DIP { DUP; CAR } }; UPDATE; DIP { DUP; DIP { CDR }; CAR }; DIP { DROP }; PAIR }; DUP; DIP { CDR; NEG; DIP { DUP; CDR; CDR; CDR }; ADD; ISNAT; IF_NONE { PUSH string "Internal: Negative total supply"; FAILWITH } { }; DIP { DUP; CDR }; DIP { DUP; DIP { CAR }; CDR }; DIP { DUP; DIP { CAR }; CDR }; DIP { DROP }; SWAP; PAIR; SWAP; PAIR; DIP { DUP; DIP { CAR }; CDR }; DIP { DROP }; SWAP; PAIR }; DROP; NIL operation; PAIR } } } } };`;
const storage = `Pair {} (Pair "${administrator}" (Pair ${pause ? 'True' : 'False'} ${supply}))`;
const nodeResult = await TezosNodeWriter.sendContractOriginationOperation(server, signer, keystore, 0, undefined, fee, freight, gas, contract, storage, TezosTypes.TezosParameterFormat.Michelson, TezosConstants.HeadBranchOffset, true);
return TezosContractUtils.clearRPCOperationGroupHash(nodeResult['operationGroupID']);
}
export async function getAccountBalance(server: string, mapid: number, account: string, balancePath: string = '$.args[0].int'): Promise<number> {
const packedKey = TezosMessageUtils.encodeBigMapKey(Buffer.from(TezosMessageUtils.writePackedData(account, 'address'), 'hex'));
const mapResult = await TezosNodeReader.getValueForBigMapKey(server, mapid, packedKey);
if (mapResult === undefined) { throw new Error(`Map ${mapid} does not contain a record for ${account}`); }
const jsonresult = JSONPath({ path: balancePath, json: mapResult });
return Number(jsonresult[0]);
}
export async function getAccountAllowance(server: string, mapid: number, account: string, source: string) {
const packedKey = TezosMessageUtils.encodeBigMapKey(Buffer.from(TezosMessageUtils.writePackedData(source, 'address'), 'hex'));
const mapResult = await TezosNodeReader.getValueForBigMapKey(server, mapid, packedKey);
if (mapResult === undefined) { throw new Error(`Map ${mapid} does not contain a record for ${source}/${account}`); }
let allowances = new Map<string, number>();
JSONPath({ path: '$.args[1][*].args', json: mapResult }).forEach(v => allowances[v[0]['string']] = Number(v[1]['int']));
return allowances[account];
}
export async function getSimpleStorage(server: string, address: string): Promise<{mapid: number, supply: number, administrator: string, paused: boolean}> {
const storageResult = await TezosNodeReader.getContractStorage(server, address);
return {
mapid: Number(JSONPath({ path: '$.args[0].int', json: storageResult })[0]),
supply: Number(JSONPath({ path: '$.args[3].int', json: storageResult })[0]),
administrator: JSONPath({ path: '$.args[1].string', json: storageResult })[0],
paused: (JSONPath({ path: '$.args[2].prim', json: storageResult })[0]).toString().toLowerCase().startsWith('t')
};
}
export async function getTokenSupply(server: string, address: string): Promise<number> {
const storageResult = await TezosNodeReader.getContractStorage(server, address);
return Number(JSONPath({ path: '$.args[3].int', json: storageResult })[0]);
}
export async function getAdministrator(server: string, address: string): Promise<string> {
const storageResult = await TezosNodeReader.getContractStorage(server, address);
return JSONPath({ path: '$.args[1].string', json: storageResult })[0];
}
export async function getPaused(server: string, address: string): Promise<boolean> {
const storageResult = await TezosNodeReader.getContractStorage(server, address);
return (JSONPath({ path: '$.args[2].prim', json: storageResult })[0]).toString().toLowerCase().startsWith('t');
}
export async function transferBalance(server: string, signer: Signer, keystore: KeyStore, contract: string, fee: number, source: string, destination: string, amount: number, gas: number, freight: number) {
const parameters = `(Pair "${source}" (Pair "${destination}" ${amount}))`;
const nodeResult = await TezosNodeWriter.sendContractInvocationOperation(server, signer, keystore, contract, 0, fee, freight, gas, 'transfer', parameters, TezosTypes.TezosParameterFormat.Michelson, TezosConstants.HeadBranchOffset, true);
return TezosContractUtils.clearRPCOperationGroupHash(nodeResult.operationGroupID);
}
export function ApproveBalanceOperation(amount: number, spender: string, counter: number, address: string, pkh: string, fee: number, gas: number = 800_000, freight: number): Transaction {
const entrypoint = 'approve';
const parameters = `(Pair "${spender}" ${amount})`;
return TezosNodeWriter.constructContractInvocationOperation(pkh, counter, address, 0, fee, freight, gas, entrypoint, parameters, TezosTypes.TezosParameterFormat.Michelson);
}
export async function approveBalance(server: string, signer: Signer, keystore: KeyStore, contract: string, fee: number, destination: string, amount: number, gas: number, freight: number) {
const parameters = `(Pair "${destination}" ${amount})`;
const nodeResult = await TezosNodeWriter.sendContractInvocationOperation(server, signer, keystore, contract, 0, fee, freight, gas, 'approve', parameters, TezosTypes.TezosParameterFormat.Michelson, TezosConstants.HeadBranchOffset, true);
return TezosContractUtils.clearRPCOperationGroupHash(nodeResult.operationGroupID);
}
export async function activateLedger(server: string, signer: Signer, keystore: KeyStore, contract: string, fee: number, gas: number, freight: number) {
const parameters = '(Right (Left (Left False)))';
const nodeResult = await TezosNodeWriter.sendContractInvocationOperation(server, signer, keystore, contract, 0, fee, freight, gas, '', parameters, TezosTypes.TezosParameterFormat.Michelson, TezosConstants.HeadBranchOffset, true);
return TezosContractUtils.clearRPCOperationGroupHash(nodeResult.operationGroupID);
}
export async function deactivateLedger(server: string, signer: Signer, keystore: KeyStore, contract: string, fee: number, gas: number, freight: number) {
const parameters = '(Right (Left (Left True)))';
const nodeResult = await TezosNodeWriter.sendContractInvocationOperation(server, signer, keystore, contract, 0, fee, freight, gas, '', parameters, TezosTypes.TezosParameterFormat.Michelson, TezosConstants.HeadBranchOffset, true);
return TezosContractUtils.clearRPCOperationGroupHash(nodeResult.operationGroupID);
}
export async function setAdministrator(server: string, signer: Signer, keystore: KeyStore, contract: string, address: string, fee: number, gas: number, freight: number) {
const parameters = `(Right (Left (Right "${address}")))`;
const nodeResult = await TezosNodeWriter.sendContractInvocationOperation(server, signer, keystore, contract, 0, fee, freight, gas, '', parameters, TezosTypes.TezosParameterFormat.Michelson, TezosConstants.HeadBranchOffset, true);
return TezosContractUtils.clearRPCOperationGroupHash(nodeResult.operationGroupID);
}
export async function mint(server: string, signer: Signer, keystore: KeyStore, contract: string, fee: number, destination: string, amount: number, gas: number = 150_000, freight: number = 5_000) {
const parameters = `(Right (Right (Right (Left (Pair "${destination}" ${amount})))))`;
const nodeResult = await TezosNodeWriter.sendContractInvocationOperation(server, signer, keystore, contract, 0, fee, freight, gas, '', parameters, TezosTypes.TezosParameterFormat.Michelson, TezosConstants.HeadBranchOffset, true);
return TezosContractUtils.clearRPCOperationGroupHash(nodeResult.operationGroupID);
}
export async function burn(server: string, signer: Signer, keystore: KeyStore, contract: string, fee: number, source: string, amount: number, gas: number, freight: number) {
const parameters = `(Right (Right (Right (Right (Pair "${source}" ${amount})))))`;
const nodeResult = await TezosNodeWriter.sendContractInvocationOperation(server, signer, keystore, contract, 0, fee, freight, gas, '', parameters, TezosTypes.TezosParameterFormat.Michelson, TezosConstants.HeadBranchOffset, true);
return TezosContractUtils.clearRPCOperationGroupHash(nodeResult.operationGroupID);
}
}