├── ExecutingEntryCallingScriptHash ├── contractA.abi.json ├── contractB.abi.json ├── contractA.avm.str ├── contractB.py ├── contractB_compiler2.0.py ├── contractB.avm.str ├── contractA.py ├── contractA_compiler2.0.py └── README.md ├── DynamicCallContract ├── CheckContract.py ├── DynamicCallContract.py ├── DynamicCallContract_compiler2.0.py ├── CheckContract_compiler2.0.py └── README.md ├── Storage_Example ├── storage_example_compiler2.0.py └── storage_example.py ├── EventTest ├── event_test.py └── event_test_compiler2.0.py ├── libs ├── Utils.py ├── SafeCheck.py └── SafeMath.py ├── MigrateDestruct ├── migrate_destroyWithinContract.py └── migrate_destroyWithinContract_compiler2.0.py ├── NativeOntIDInvoke ├── nativeOntIDInvoke.py └── nativeOntIDInvoke_compiler2.0.py ├── Struct_Example ├── struct_example.py └── struct_example_compiler2.0.py ├── ListandMapSample ├── listAndMapSample.py ├── listAndMapSample_compiler2.0.py └── README.md ├── compile_contract.py ├── NativeAssetInvoke ├── native_asset_invoke.py └── native_asset_invoke_compiler2.0.py ├── Static_Call_Oep4 ├── static_call_Oep4.py └── static_call_Oep4_compiler2.0.py ├── OEP35Sample ├── README.md └── TokenProxy.py ├── README.md ├── UnboundWithdrawONG ├── unboundWithdrawONG.py └── unboundWithdrawONG_compiler2.0.py ├── OEP5Sample ├── README.md ├── OEP5Sample.py └── OEP5Sample_compiler2.0.py ├── OEP4Sample ├── OEP4Sample.py └── OEP4Sample_compiler2.0.py ├── LICENSE └── OEP8Sample ├── OEP8Sample.py └── OEP8Sample_compiler2.0.py /ExecutingEntryCallingScriptHash/contractA.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "CompilerVersion": "1.0.2", 3 | "hash": "b0339f7bea0b2a38ab7294bd41106d354a6b489a", 4 | "entrypoint": "Main", 5 | "functions": [] 6 | } -------------------------------------------------------------------------------- /ExecutingEntryCallingScriptHash/contractB.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "CompilerVersion": "1.0.2", 3 | "hash": "91396642e3ec943bf749eb858ec7edd118ee54be", 4 | "entrypoint": "Main", 5 | "functions": [] 6 | } -------------------------------------------------------------------------------- /DynamicCallContract/CheckContract.py: -------------------------------------------------------------------------------- 1 | from boa.interop.System.Runtime import Notify 2 | from boa.interop.System.Storage import Put, Get, GetContext 3 | TOTAL_SUPPLY = 'totalsupply' 4 | 5 | def Main(operation, args): 6 | 7 | if operation == "check": 8 | return check() 9 | return False 10 | 11 | def check(): 12 | Put(GetContext(), TOTAL_SUPPLY, 10000) 13 | Notify(["11_check", 10000]) 14 | return Get(GetContext(), TOTAL_SUPPLY) 15 | -------------------------------------------------------------------------------- /Storage_Example/storage_example_compiler2.0.py: -------------------------------------------------------------------------------- 1 | OntCversion = '2.0.0' 2 | 3 | from ontology.interop.System.Storage import Put, GetContext, Get, Delete 4 | from template_contract_test.libs.SafeMath import * 5 | 6 | ctx = GetContext() 7 | 8 | 9 | def Main(operation, args): 10 | if operation == "TestStorage": 11 | return TestStorage() 12 | 13 | 14 | def TestStorage(): 15 | Put(ctx, "key", 100) 16 | v = Get(ctx, "key") 17 | Notify(v) 18 | 19 | Delete(ctx, "key") 20 | Notify(Get(ctx, "key")) -------------------------------------------------------------------------------- /Storage_Example/storage_example.py: -------------------------------------------------------------------------------- 1 | from boa.interop.System.Runtime import Notify 2 | from boa.interop.System.Storage import Put, GetContext, Get, Delete 3 | from template_contract_test.libs.SafeMath import * 4 | 5 | ctx = GetContext() 6 | 7 | 8 | def Main(operation, args): 9 | if operation == "testStorage": 10 | return testStorage() 11 | return False 12 | 13 | def testStorage(): 14 | Put(ctx, "key", 100) 15 | v = Get(ctx, "key") 16 | Notify(v) 17 | 18 | Delete(ctx, "key") 19 | Notify(Get(ctx, "key")) 20 | return True -------------------------------------------------------------------------------- /EventTest/event_test.py: -------------------------------------------------------------------------------- 1 | from boa.interop.System.Action import RegisterAction 2 | 3 | Transfer = RegisterAction('transfer_test', 'a', 'b', 'c') 4 | Refund = RegisterAction('refund_test', 'to', 'amount') 5 | 6 | 7 | def Main(operation, args): 8 | if operation == "test1": 9 | return test() 10 | if operation =="test2": 11 | return test1() 12 | return False 13 | 14 | def test(): 15 | a = 2 16 | b = 5 17 | c = a + b 18 | Transfer(a, b, c) 19 | return True 20 | 21 | def test1(): 22 | to = 'somebody' 23 | amount = 52 24 | Refund(to, amount) 25 | return True 26 | -------------------------------------------------------------------------------- /EventTest/event_test_compiler2.0.py: -------------------------------------------------------------------------------- 1 | OntCversion = '2.0.0' 2 | from ontology.interop.System.Action import RegisterAction 3 | 4 | Transfer = RegisterAction('transfer', 'a', 'b', 'c') 5 | Refund = RegisterAction('refund_test', 'to', 'amount') 6 | 7 | 8 | def Main(operation, args): 9 | if operation == "test1": 10 | return test() 11 | if operation =="test2": 12 | return test1() 13 | return False 14 | 15 | def test(): 16 | a = 2 17 | b = 5 18 | c = a + b 19 | Transfer(a, b, c) 20 | return True 21 | 22 | def test1(): 23 | to = 'somebody' 24 | amount = 52 25 | Refund(to, amount) 26 | return True 27 | -------------------------------------------------------------------------------- /libs/Utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*-""" 2 | """ 3 | You can import this file and methods in your smart contract, 4 | you can also add utility methods within this function based on your needs. 5 | """ 6 | from boa.interop.System.Storage import Delete, Put 7 | 8 | def Revert(): 9 | """ 10 | Revert the transaction. The opcodes of this function is `09f7f6f5f4f3f2f1f000f0`, 11 | but it will be changed to `ffffffffffffffffffffff` since opcode THROW doesn't 12 | work, so, revert by calling unused opcode. 13 | """ 14 | raise Exception(0xF1F1F2F2F3F3F4F4) 15 | 16 | def SafePut(context, key, value): 17 | if value == 0: 18 | Delete(context, key) 19 | else: 20 | Put(context, key, value) 21 | 22 | 23 | -------------------------------------------------------------------------------- /MigrateDestruct/migrate_destroyWithinContract.py: -------------------------------------------------------------------------------- 1 | from boa.interop.Ontology.Contract import Migrate 2 | from boa.interop.Ontology.Contract import Destroy 3 | from boa.interop.System.Runtime import Notify 4 | 5 | 6 | def Main(operation, args): 7 | if operation == "DestroyContract": 8 | return DestroyContract() 9 | if operation == "MigrateContract": 10 | if args[0] != 1: 11 | Notify("param error") 12 | return False 13 | return MigrateContract(args[0]) 14 | return False 15 | 16 | def DestroyContract(): 17 | Destroy() 18 | Notify(["Destory"]) 19 | return True 20 | 21 | def MigrateContract(code): 22 | """ 23 | Note that the existing contract will be replaced by the newly migrated contract 24 | :param code: your avm code 25 | :return: 26 | """ 27 | res = Migrate(code, "", "", "", "", "", "") 28 | if res: 29 | Notify(["Migrate successfully"]) 30 | return True 31 | else: 32 | return False -------------------------------------------------------------------------------- /NativeOntIDInvoke/nativeOntIDInvoke.py: -------------------------------------------------------------------------------- 1 | from boa.interop.Ontology.Native import Invoke 2 | from boa.builtins import ToScriptHash, state 3 | from boa.interop.System.Runtime import Notify 4 | from boa.interop.System.ExecutionEngine import GetExecutingScriptHash 5 | 6 | # ONT Big endian Script Hash: 0x0100000000000000000000000000000000000000 7 | OntContract = ToScriptHash("AFmseVrdL9f9oyCzZefL9tG6UbvhUMqNMV") 8 | # ONG Big endian Script Hash: 0x0200000000000000000000000000000000000000 9 | OngContract = ToScriptHash("AFmseVrdL9f9oyCzZefL9tG6UbvhfRZMHJ") 10 | # ONTID Big endian Script Hash: 0x0300000000000000000000000000000000000000 11 | OntIDContract = ToScriptHash("AFmseVrdL9f9oyCzZefL9tG6Ubvho7BUwN") 12 | 13 | def Main(operation, args): 14 | if operation == "preInvokeGetDDO": 15 | if len(args) != 1: 16 | Notify("wrong params") 17 | return False 18 | return preInvokeGetDDO(args[0]) 19 | return False 20 | 21 | def preInvokeGetDDO(id): 22 | param = state(id) 23 | res = Invoke(0, OntIDContract, "getDDO", param) 24 | return res -------------------------------------------------------------------------------- /ExecutingEntryCallingScriptHash/contractA.avm.str: -------------------------------------------------------------------------------- 1 | 5bc56b6a00527ac46a51527ac46a00c307696e766f6b65419c6424006a51c300c36a52527ac46a51c351c36a53527ac46a52c36a53c37c6512016c7566616a00c309636865636b486173689c640900650b006c756661006c756658c56b682d53797374656d2e457865637574696f6e456e67696e652e476574457865637574696e6753637269707448617368616a00527ac40d3131315f636865636b4861736851c176c9681553797374656d2e52756e74696d652e4e6f7469667961682b53797374656d2e457865637574696f6e456e67696e652e47657443616c6c696e6753637269707448617368616a51527ac4682953797374656d2e457865637574696f6e456e67696e652e476574456e74727953637269707448617368616a52527ac46a51c36a52c36a00c353c176c9681553797374656d2e52756e74696d652e4e6f7469667961516c756659c56b6a00527ac46a51527ac4682d53797374656d2e457865637574696f6e456e67696e652e476574457865637574696e6753637269707448617368616a52527ac4682b53797374656d2e457865637574696f6e456e67696e652e47657443616c6c696e6753637269707448617368616a53527ac4682953797374656d2e457865637574696f6e456e67696e652e476574456e74727953637269707448617368616a54527ac40b3131315f696e766f6b65416a53c36a54c36a52c354c176c9681553797374656d2e52756e74696d652e4e6f74696679616a00c36a51c37c679a9bb5a8a3c19cefc12f45c311f8cad2dad1994d6c7566 -------------------------------------------------------------------------------- /ExecutingEntryCallingScriptHash/contractB.py: -------------------------------------------------------------------------------- 1 | from boa.interop.System.ExecutionEngine import GetExecutingScriptHash, GetCallingScriptHash, GetEntryScriptHash 2 | from boa.interop.System.Runtime import CheckWitness, GetTime, Notify, Serialize, Deserialize 3 | 4 | 5 | 6 | ContractAddress = GetExecutingScriptHash() 7 | def Main(opration, args): 8 | if opration == "invokeB": 9 | return invokeB(args[0]) 10 | if opration == "avoidToBeInvokedByContract": 11 | return avoidToBeInvokedByContract() 12 | return False 13 | 14 | 15 | def invokeB(param): 16 | Notify(["111_invokeB", param]) 17 | # to prevent hack from other contract 18 | callerHash = GetCallingScriptHash() 19 | entryHash = GetEntryScriptHash() 20 | Notify([callerHash, entryHash, ContractAddress]) 21 | return True 22 | 23 | def avoidToBeInvokedByContract(): 24 | callerHash = GetCallingScriptHash() 25 | entryHash = GetEntryScriptHash() 26 | if callerHash != entryHash: 27 | Notify(["You are not allowed to invoke this method through contract"]) 28 | return False 29 | else: 30 | Notify(["You can implement what you need to do here!"]) 31 | return True -------------------------------------------------------------------------------- /NativeOntIDInvoke/nativeOntIDInvoke_compiler2.0.py: -------------------------------------------------------------------------------- 1 | OntCversion = '2.0.0' 2 | from ontology.interop.Ontology.Native import Invoke 3 | from ontology.builtins import state 4 | from ontology.interop.System.Runtime import Notify 5 | from ontology.interop.System.ExecutionEngine import GetExecutingScriptHash 6 | from ontology.interop.Ontology.Runtime import Base58ToAddress 7 | 8 | # ONT Big endian Script Hash: 0x0100000000000000000000000000000000000000 9 | OntContract = Base58ToAddress("AFmseVrdL9f9oyCzZefL9tG6UbvhUMqNMV") 10 | # ONG Big endian Script Hash: 0x0200000000000000000000000000000000000000 11 | OngContract = Base58ToAddress("AFmseVrdL9f9oyCzZefL9tG6UbvhfRZMHJ") 12 | # ONTID Big endian Script Hash: 0x0300000000000000000000000000000000000000 13 | OntIDContract = Base58ToAddress("AFmseVrdL9f9oyCzZefL9tG6Ubvho7BUwN") 14 | 15 | def Main(operation, args): 16 | if operation == "preInvokeGetDDO": 17 | if len(args) != 1: 18 | Notify("wrong params") 19 | return False 20 | return preInvokeGetDDO(args[0]) 21 | return False 22 | 23 | def preInvokeGetDDO(id): 24 | param = state(id) 25 | res = Invoke(0, OntIDContract, "getDDO", param) 26 | return res -------------------------------------------------------------------------------- /ExecutingEntryCallingScriptHash/contractB_compiler2.0.py: -------------------------------------------------------------------------------- 1 | OntCversion = '2.0.0' 2 | from ontology.interop.System.ExecutionEngine import GetExecutingScriptHash, GetCallingScriptHash, GetEntryScriptHash 3 | from ontology.interop.System.Runtime import CheckWitness, GetTime, Notify, Serialize, Deserialize 4 | 5 | 6 | 7 | ContractAddress = GetExecutingScriptHash() 8 | def Main(opration, args): 9 | if opration == "invokeB": 10 | return invokeB(args[0]) 11 | if opration == "avoidToBeInvokedByContract": 12 | return avoidToBeInvokedByContract() 13 | return False 14 | 15 | 16 | def invokeB(param): 17 | Notify(["111_invokeB", param]) 18 | # to prevent hack from other contract 19 | callerHash = GetCallingScriptHash() 20 | entryHash = GetEntryScriptHash() 21 | Notify([callerHash, entryHash, ContractAddress]) 22 | return True 23 | 24 | def avoidToBeInvokedByContract(): 25 | callerHash = GetCallingScriptHash() 26 | entryHash = GetEntryScriptHash() 27 | if callerHash != entryHash: 28 | Notify(["You are not allowed to invoke this method through contract"]) 29 | return False 30 | else: 31 | Notify(["You can implement what you need to do here!"]) 32 | return True -------------------------------------------------------------------------------- /ExecutingEntryCallingScriptHash/contractB.avm.str: -------------------------------------------------------------------------------- 1 | 59c56b6a00527ac46a51527ac46a00c307696e766f6b65429c640e006a51c300c36551016c7566616a00c31a61766f6964546f4265496e766f6b65644279436f6e74726163749c640900650b006c756661006c756659c56b682b53797374656d2e457865637574696f6e456e67696e652e47657443616c6c696e6753637269707448617368616a00527ac4682953797374656d2e457865637574696f6e456e67696e652e476574456e74727953637269707448617368616a51527ac46a00c36a51c39e645e003a596f7520617265206e6f7420616c6c6f77656420746f20696e766f6b652074686973206d6574686f64207468726f75676820636f6e747261637451c176c9681553797374656d2e52756e74696d652e4e6f7469667961006c7566612b596f752063616e20696d706c656d656e74207768617420796f75206e65656420746f20646f20686572652151c176c9681553797374656d2e52756e74696d652e4e6f7469667961516c7566006c756659c56b6a00527ac4682d53797374656d2e457865637574696f6e456e67696e652e476574457865637574696e6753637269707448617368616a51527ac40b3131315f696e766f6b65426a00c352c176c9681553797374656d2e52756e74696d652e4e6f7469667961682b53797374656d2e457865637574696f6e456e67696e652e47657443616c6c696e6753637269707448617368616a52527ac4682953797374656d2e457865637574696f6e456e67696e652e476574456e74727953637269707448617368616a53527ac46a52c36a53c36a51c353c176c9681553797374656d2e52756e74696d652e4e6f7469667961516c7566 -------------------------------------------------------------------------------- /libs/SafeCheck.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*-""" 2 | """ 3 | You can import this file and methods in your smart contract, 4 | you can also add safe check methods within this function based on your needs. 5 | """ 6 | 7 | from template_contract_test.libs.Utils import Revert 8 | from boa.interop.System.Runtime import CheckWitness 9 | 10 | def Require(condition): 11 | """ 12 | If condition is not satisfied, return false 13 | :param condition: required condition 14 | :return: True or false 15 | """ 16 | if not condition: 17 | Revert() 18 | return True 19 | 20 | def RequireScriptHash(key): 21 | """ 22 | Checks the bytearray parameter is script hash or not. Script Hash 23 | length should be equal to 20. 24 | :param key: bytearray parameter to check script hash format. 25 | :return: True if script hash or revert the transaction. 26 | """ 27 | Require(len(key) == 20) 28 | return True 29 | 30 | def RequireWitness(witness): 31 | """ 32 | Checks the transaction sender is equal to the witness. If not 33 | satisfying, revert the transaction. 34 | :param witness: required transaction sender 35 | :return: True if transaction sender or revert the transaction. 36 | """ 37 | Require(CheckWitness(witness)) 38 | return True -------------------------------------------------------------------------------- /Struct_Example/struct_example.py: -------------------------------------------------------------------------------- 1 | from boa.interop.System.Runtime import Notify, CheckWitness 2 | from boa.interop.Ontology.Native import Invoke 3 | from boa.builtins import state 4 | 5 | contractAddress = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') 6 | 7 | 8 | def Main(operation, args): 9 | if operation == 'transfer': 10 | fromacct = args[0] 11 | toacct = args[1] 12 | amount = args[2] 13 | return transferONT(fromacct,toacct,amount) 14 | 15 | 16 | return False 17 | 18 | 19 | def transferONT(fromacct,toacct,amount): 20 | """ 21 | transfer ONT 22 | :param fromacct: 23 | :param toacct: 24 | :param amount: 25 | :return: 26 | """ 27 | if CheckWitness(fromacct): 28 | 29 | param = makeState(fromacct, toacct, amount) 30 | res = Invoke(1, contractAddress, 'transfer', [param]) 31 | Notify(["11111",res]) 32 | 33 | if res and res == b'\x01': 34 | Notify('transfer succeed') 35 | return True 36 | else: 37 | Notify('transfer failed') 38 | 39 | return False 40 | 41 | else: 42 | Notify('checkWitness failed') 43 | return False 44 | 45 | 46 | def makeState(fromacct,toacct,amount): 47 | return state(fromacct, toacct, amount) -------------------------------------------------------------------------------- /ExecutingEntryCallingScriptHash/contractA.py: -------------------------------------------------------------------------------- 1 | from boa.interop.System.App import RegisterAppCall 2 | from boa.interop.System.ExecutionEngine import GetExecutingScriptHash, GetCallingScriptHash, GetEntryScriptHash 3 | from boa.interop.System.Runtime import CheckWitness, GetTime, Notify, Serialize, Deserialize 4 | 5 | 6 | # Here "4d99d1dad2caf811c3452fc1ef9cc1a3a8b59b9a" should your contract hash, pls note it's not reversed hash 7 | ContractB = RegisterAppCall('4d99d1dad2caf811c3452fc1ef9cc1a3a8b59b9a', 'operation', 'args') 8 | 9 | ContractAddress = GetExecutingScriptHash() 10 | 11 | 12 | def Main(opration, args): 13 | if opration == "invokeA": 14 | opt = args[0] 15 | params = args[1] 16 | return invokeA(opt, params) 17 | if opration == "checkHash": 18 | return checkHash() 19 | return False 20 | 21 | def invokeA(operation, params): 22 | callerHash = GetCallingScriptHash() 23 | entryHash = GetEntryScriptHash() 24 | Notify(["111_invokeA",callerHash, entryHash, ContractAddress]) 25 | return ContractB(operation, params) 26 | 27 | 28 | def checkHash(): 29 | Notify(["111_checkHash"]) 30 | # to prevent hack from other contract 31 | callerHash = GetCallingScriptHash() 32 | entryHash = GetEntryScriptHash() 33 | Notify([callerHash, entryHash, ContractAddress]) 34 | return True 35 | -------------------------------------------------------------------------------- /ExecutingEntryCallingScriptHash/contractA_compiler2.0.py: -------------------------------------------------------------------------------- 1 | OntCversion = '2.0.0' 2 | from ontology.interop.System.App import RegisterAppCall 3 | from ontology.interop.System.ExecutionEngine import GetExecutingScriptHash, GetCallingScriptHash, GetEntryScriptHash 4 | from ontology.interop.System.Runtime import CheckWitness, GetTime, Notify, Serialize, Deserialize 5 | 6 | 7 | # Here "4d99d1dad2caf811c3452fc1ef9cc1a3a8b59b9a" should your contract hash, pls note it's not reversed hash 8 | ContractB = RegisterAppCall('4d99d1dad2caf811c3452fc1ef9cc1a3a8b59b9a', 'operation', 'args') 9 | 10 | ContractAddress = GetExecutingScriptHash() 11 | 12 | 13 | def Main(opration, args): 14 | if opration == "invokeA": 15 | opt = args[0] 16 | params = args[1] 17 | return invokeA(opt, params) 18 | if opration == "checkHash": 19 | return checkHash() 20 | return False 21 | 22 | def invokeA(operation, params): 23 | callerHash = GetCallingScriptHash() 24 | entryHash = GetEntryScriptHash() 25 | Notify(["111_invokeA",callerHash, entryHash, ContractAddress]) 26 | return ContractB(operation, params) 27 | 28 | 29 | def checkHash(): 30 | Notify(["111_checkHash"]) 31 | # to prevent hack from other contract 32 | callerHash = GetCallingScriptHash() 33 | entryHash = GetEntryScriptHash() 34 | Notify([callerHash, entryHash, ContractAddress]) 35 | return True 36 | -------------------------------------------------------------------------------- /Struct_Example/struct_example_compiler2.0.py: -------------------------------------------------------------------------------- 1 | OntCversion = '2.0.0' 2 | from ontology.interop.System.Runtime import Notify, CheckWitness 3 | from ontology.interop.Ontology.Native import Invoke 4 | from ontology.builtins import state 5 | 6 | contractAddress = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') 7 | 8 | 9 | def Main(operation, args): 10 | if operation == 'transfer': 11 | fromacct = args[0] 12 | toacct = args[1] 13 | amount = args[2] 14 | return transferONT(fromacct,toacct,amount) 15 | 16 | 17 | return False 18 | 19 | 20 | def transferONT(fromacct,toacct,amount): 21 | """ 22 | transfer ONT 23 | :param fromacct: 24 | :param toacct: 25 | :param amount: 26 | :return: 27 | """ 28 | if CheckWitness(fromacct): 29 | 30 | param = makeState(fromacct, toacct, amount) 31 | res = Invoke(0, contractAddress, 'transfer', [fromacct, toacct, amount]) 32 | Notify(["11111",res]) 33 | 34 | if res and res == b'\x01': 35 | Notify('transfer succeed') 36 | return True 37 | else: 38 | Notify('transfer failed') 39 | 40 | return False 41 | 42 | else: 43 | Notify('checkWitness failed') 44 | return False 45 | 46 | 47 | def makeState(fromacct,toacct,amount): 48 | return state(fromacct, toacct, amount) -------------------------------------------------------------------------------- /DynamicCallContract/DynamicCallContract.py: -------------------------------------------------------------------------------- 1 | from boa.interop.System.App import RegisterAppCall, DynamicAppCall 2 | from boa.interop.System.Runtime import Log, Notify 3 | 4 | 5 | CallContract = RegisterAppCall('b16e976491982ddccd195dd73bd952a423a5e833', 'operation', 'args') 6 | 7 | # input = b'\x33...\xb1'' 8 | # b16e976491982ddccd195dd73bd952a423a5e833 9 | 10 | NAME = "Dynamic" 11 | 12 | def Main(operation, args): 13 | if operation == "DynamicCallContract": 14 | if len(args) != 3: 15 | return False 16 | revesedContractAddress = args[0] 17 | opt = args[1] 18 | params = args[2] 19 | return DynamicCallContract(revesedContractAddress, opt, params) 20 | if operation == "StaticCallContract": 21 | opt = args[0] 22 | params = args[1] 23 | return StaticCallContract(opt,params) 24 | 25 | if operation == "name": 26 | return getName() 27 | return False 28 | 29 | def DynamicCallContract(revesedContractAddress, operation, params): 30 | Notify(["bytearray: ", revesedContractAddress]) 31 | res = DynamicAppCall(revesedContractAddress, operation, params) 32 | Notify(["111_DynamicCall", revesedContractAddress, operation, params]) 33 | Notify(["222_DynamicCall", res]) 34 | return res 35 | 36 | def StaticCallContract(opt, params): 37 | return CallContract(opt, params) 38 | 39 | 40 | 41 | def getName(): 42 | return NAME -------------------------------------------------------------------------------- /MigrateDestruct/migrate_destroyWithinContract_compiler2.0.py: -------------------------------------------------------------------------------- 1 | OntCversion = '2.0.0' 2 | from ontology.interop.Ontology.Contract import Migrate 3 | # from ontology.interop.Ontology.Contract import Destroy 4 | from ontology.interop.System.Runtime import Notify 5 | from ontology.interop.System.Storage import Put, GetContext, Get 6 | 7 | KEY = "KEY" 8 | NAME = "SecondName" 9 | 10 | def Main(operation, args): 11 | # if operation == "DestroyContract": 12 | # return DestroyContract() 13 | if operation == "MigrateContract": 14 | if len(args) != 1: 15 | Notify("param error") 16 | return False 17 | return MigrateContract(args[0]) 18 | if operation == "put": 19 | return put() 20 | if operation == "get": 21 | return get() 22 | if operation == "name": 23 | return NAME 24 | 25 | # def DestroyContract(): 26 | # Destroy() 27 | # Notify(["Destory"]) 28 | # return True 29 | 30 | def MigrateContract(code): 31 | """ 32 | Note that the existing contract will be replaced by the newly migrated contract 33 | :param code: your avm code 34 | :return: 35 | """ 36 | res = Migrate(code, True, "name", "version", "author", "email", "description") 37 | assert(res) 38 | Notify(["Migrate successfully"]) 39 | return True 40 | 41 | def get(): 42 | return Get(GetContext(), KEY) 43 | 44 | def put(): 45 | Put(GetContext(), KEY, 898) 46 | return True 47 | 48 | -------------------------------------------------------------------------------- /ListandMapSample/listAndMapSample.py: -------------------------------------------------------------------------------- 1 | from boa.interop.System.Storage import Put, Get, GetContext 2 | from boa.interop.System.Runtime import Notify, Serialize, Deserialize 3 | 4 | LISTKEY = "List" 5 | MAPKEY = "Map" 6 | 7 | def Main(operation, args): 8 | if operation == "init": 9 | return init() 10 | if operation == "addList": 11 | elementList = args[0] 12 | return addList(elementList) 13 | if operation == "addMap": 14 | key = args[0] 15 | value = args[1] 16 | return addMap(key, value) 17 | return False 18 | 19 | def init(): 20 | # init list 21 | list1 = [1,2,3] 22 | list1Info = Serialize(list1) 23 | Put(GetContext(), LISTKEY, list1Info) 24 | # init map 25 | map1 = { 26 | "key1":1, 27 | "key2":2 28 | } 29 | map1Info = Serialize(map1) 30 | Put(GetContext(), MAPKEY, map1Info) 31 | 32 | Notify(["init list is ",list1]) 33 | Notify(["init map is ", map1["key1"], map1["key2"]]) 34 | 35 | return True 36 | 37 | def addList(listToBeAppend): 38 | list1Info = Get(GetContext(), LISTKEY) 39 | list1 = Deserialize(list1Info) 40 | 41 | Notify(["before add, list is ", list1]) 42 | 43 | for element in listToBeAppend: 44 | list1.append(element) 45 | list1Info = Serialize(list1) 46 | Put(GetContext(), LISTKEY, list1Info) 47 | Notify(["after add, list is ", list1]) 48 | 49 | return list1 50 | 51 | 52 | def addMap(key, value): 53 | map1Info = Get(GetContext(), MAPKEY) 54 | map1 = Deserialize(map1Info) 55 | 56 | Notify(["before add, map is ", map1["key1"], map1["key2"]]) 57 | 58 | map1[key] = value 59 | map1.remove("key1") 60 | 61 | map1Info = Serialize(map1) 62 | Put(GetContext(), MAPKEY, map1Info) 63 | Notify(["after add, map is ", map1["key2"], map1[key]]) 64 | 65 | return True 66 | -------------------------------------------------------------------------------- /libs/SafeMath.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*-""" 2 | """ 3 | You can import this file and methods in your smart contract, 4 | you can also add safe math methods within this function based on your needs. 5 | """ 6 | from template_contract_test.libs.SafeCheck import Require 7 | from boa.interop.System.Runtime import Notify 8 | 9 | def Add(a, b): 10 | """ 11 | Adds two numbers, throws on overflow. 12 | """ 13 | c = a + b 14 | Require(c >= a) 15 | return c 16 | 17 | def Sub(a, b): 18 | """ 19 | Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). 20 | :param a: operand a 21 | :param b: operand b 22 | :return: a - b if a - b > 0 or revert the transaction. 23 | """ 24 | Require(a>=b) 25 | return a-b 26 | 27 | def Mul(a, b): 28 | """ 29 | Multiplies two numbers, throws on overflow. 30 | :param a: operand a 31 | :param b: operand b 32 | :return: a - b if a - b > 0 or revert the transaction. 33 | """ 34 | if a == 0: 35 | return 0 36 | c = a * b 37 | Require(c / a == b) 38 | return c 39 | 40 | def Div(a, b): 41 | """ 42 | Integer division of two numbers, truncating the quotient. 43 | """ 44 | Require(b > 0) 45 | c = a / b 46 | return c 47 | 48 | def Pwr(a, b): 49 | """ 50 | a to the power of b 51 | :param a the base 52 | :param b the power value 53 | :return a^b 54 | """ 55 | c = 0 56 | if a == 0: 57 | c = 0 58 | elif b == 0: 59 | c = 1 60 | else: 61 | i = 0 62 | c = 1 63 | while i < b: 64 | c = Mul(c, a) 65 | i = i + 1 66 | return c 67 | 68 | def Sqrt(a): 69 | """ 70 | Return sqrt of a 71 | :param a: 72 | :return: sqrt(a) 73 | """ 74 | c = Div(Add(a, 1), 2) 75 | b = a 76 | while(c < b): 77 | b = c 78 | c = Div(Add(Div(a, c), c), 2) 79 | return c -------------------------------------------------------------------------------- /ListandMapSample/listAndMapSample_compiler2.0.py: -------------------------------------------------------------------------------- 1 | OntCversion = '2.0.0' 2 | from ontology.interop.System.Storage import Put, Get, GetContext 3 | from ontology.interop.System.Runtime import Notify, Serialize, Deserialize 4 | 5 | LISTKEY = "List" 6 | MAPKEY = "Map" 7 | 8 | def Main(operation, args): 9 | if operation == "init": 10 | return init() 11 | if operation == "addList": 12 | elementList = args[0] 13 | return addList(elementList) 14 | if operation == "addMap": 15 | key = args[0] 16 | value = args[1] 17 | return addMap(key, value) 18 | return False 19 | 20 | def init(): 21 | # init list 22 | list1 = [1,2,3] 23 | list1Info = Serialize(list1) 24 | Put(GetContext(), LISTKEY, list1Info) 25 | # init map 26 | map1 = { 27 | "key1":1, 28 | "key2":2 29 | } 30 | map1Info = Serialize(map1) 31 | Put(GetContext(), MAPKEY, map1Info) 32 | 33 | Notify(["init list is ",list1]) 34 | Notify(["init map is ", map1["key1"], map1["key2"]]) 35 | 36 | return True 37 | 38 | def addList(listToBeAppend): 39 | list1Info = Get(GetContext(), LISTKEY) 40 | list1 = Deserialize(list1Info) 41 | 42 | Notify(["before add, list is ", list1]) 43 | 44 | for element in listToBeAppend: 45 | list1.append(element) 46 | list1Info = Serialize(list1) 47 | Put(GetContext(), LISTKEY, list1Info) 48 | Notify(["after add, list is ", list1]) 49 | 50 | return list1 51 | 52 | 53 | def addMap(key, value): 54 | map1Info = Get(GetContext(), MAPKEY) 55 | map1 = Deserialize(map1Info) 56 | 57 | Notify(["before add, map is ", map1["key1"], map1["key2"]]) 58 | 59 | map1[key] = value 60 | map1.remove("key1") 61 | 62 | map1Info = Serialize(map1) 63 | Put(GetContext(), MAPKEY, map1Info) 64 | Notify(["after add, map is ", map1["key2"], map1[key]]) 65 | 66 | return True 67 | -------------------------------------------------------------------------------- /compile_contract.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | from boa.compiler import Compiler 3 | 4 | def hexlify_avm(blob): 5 | return binascii.hexlify(blob).decode('ascii') 6 | 7 | def read_avm(filename): 8 | with open(filename, 'rb') as f: 9 | return hexlify_avm(f.read()) 10 | 11 | def save_avm(filename, a): 12 | with open(filename,'w') as f: 13 | f.write(a) 14 | 15 | 16 | def run(file_path, file_name): 17 | """ 18 | Read the source py file and compile it, save it into readable avm file 19 | :param file_path: the folder of file 20 | :param file_name: the source file name without ".py"-suffix 21 | :return: 22 | """ 23 | template_file = template_file_path + template_file_name 24 | template_file_name_py = template_file + ".py" 25 | Compiler.load_and_save(template_file_name_py) 26 | # Out_readable_Avm.avm is the output file that we are going to use when deploying our contract 27 | readable_out_avm_file= template_file_path + "Out_readable_Avm.avm" 28 | save_avm(readable_out_avm_file, read_avm(template_file + ".avm")) 29 | 30 | 31 | if __name__ == '__main__': 32 | """ set up the compiled file path and file name""" 33 | 34 | template_file_path = "./OEP4Sample/" 35 | template_file_name = "OEP4Sample" 36 | 37 | # template_file_path = "./OEP5Sample/" 38 | # template_file_name = "OEP5Sample" 39 | 40 | 41 | # template_file_path = "./MigrateDestruct/" 42 | # template_file_name = "migrate_destroyWithinContract" 43 | 44 | # template_file_path = "./Static_Call_Oep4/" 45 | # template_file_name = "static_call_Oep4" 46 | 47 | # template_file_path = "./Storage_Example/" 48 | # template_file_name = "storage_example" 49 | 50 | # template_file_path = "./Struct_Example/" 51 | # template_file_name = "struct_example" 52 | 53 | # template_file_path = "./NativeAssetInvoke/" 54 | # template_file_name = "native_asset_invoke" 55 | 56 | # template_file_path = "./EventTest/" 57 | # template_file_name = "event_test" 58 | 59 | # Compile the designated file.py 60 | run(template_file_path, template_file_name) 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /DynamicCallContract/DynamicCallContract_compiler2.0.py: -------------------------------------------------------------------------------- 1 | OntCversion = '2.0.0' 2 | from ontology.interop.System.App import RegisterAppCall, DynamicAppCall 3 | from ontology.interop.System.Runtime import Log, Notify 4 | from ontology.interop.System.ExecutionEngine import GetExecutingScriptHash 5 | 6 | CallContract = RegisterAppCall('ebe0ff4ee0524c2dabcd1331c3c842896bf40b97', 'operation', 'args') 7 | selfContractAddr_ = GetExecutingScriptHash() 8 | 9 | # input = b'\x33...\xb1'' 10 | # b16e976491982ddccd195dd73bd952a423a5e833 11 | 12 | NAME = "Dynamic" 13 | 14 | def Main(operation, args): 15 | if operation == "DynamicCallContract": 16 | if len(args) != 3: 17 | return False 18 | revesedContractAddress = args[0] 19 | opt = args[1] 20 | params = args[2] 21 | return DynamicCallContract(revesedContractAddress, opt, params) 22 | if operation == "StaticCallContract": 23 | opt = args[0] 24 | params = args[1] 25 | return StaticCallContract(opt,params) 26 | 27 | if operation == "getName": 28 | return getName() 29 | 30 | if operation == "DynamicSendOng": 31 | revesedContractAddress = args[0] 32 | ongAmount = args[1] 33 | return DynamicSendOng(revesedContractAddress, ongAmount) 34 | 35 | return False 36 | 37 | 38 | def DynamicCallContract(revesedContractAddress, operation, params): 39 | Notify(["bytearray: ", revesedContractAddress]) 40 | res = DynamicAppCall(revesedContractAddress, operation, params) 41 | Notify(["111_DynamicCall", revesedContractAddress, operation, params]) 42 | Notify(["222_DynamicCall", res]) 43 | return res 44 | 45 | def DynamicSendOng(revesedContractAddress, ongAmount): 46 | params = [selfContractAddr_, ongAmount] 47 | Notify(["params: ", params]) 48 | res = DynamicAppCall(revesedContractAddress, "receiveONG", params) 49 | Notify(["111_DynamicSendOng", revesedContractAddress, "receiveONG", params]) 50 | Notify(["222_DynamicSendOng", res]) 51 | return res 52 | 53 | def StaticCallContract(opt, params): 54 | return CallContract(opt, params) 55 | 56 | def getName(): 57 | return NAME -------------------------------------------------------------------------------- /DynamicCallContract/CheckContract_compiler2.0.py: -------------------------------------------------------------------------------- 1 | OntCversion = '2.0.0' 2 | from ontology.interop.System.Runtime import Notify 3 | from ontology.interop.System.Storage import Put, Get, GetContext 4 | from ontology.interop.System.ExecutionEngine import GetExecutingScriptHash 5 | from ontology.builtins import concat, state 6 | from ontology.interop.System.Runtime import CheckWitness, Notify, GetTime 7 | from ontology.interop.Ontology.Runtime import Base58ToAddress 8 | from ontology.interop.System.Storage import Get, GetContext, Put, Delete 9 | from ontology.interop.Ontology.Native import Invoke 10 | from ontology.interop.Ontology.Contract import Migrate 11 | TOTAL_SUPPLY = 'totalsupply' 12 | ONGContractAddress_ = Base58ToAddress("AFmseVrdL9f9oyCzZefL9tG6UbvhfRZMHJ") 13 | selfContractAddr_ = GetExecutingScriptHash() 14 | 15 | def Main(operation, args): 16 | 17 | if operation == "check": 18 | return check() 19 | if operation == "receiveONG": 20 | account = args[0] 21 | ongAmount = args[1] 22 | return receiveONG(account, ongAmount) 23 | return False 24 | 25 | 26 | def check(): 27 | Put(GetContext(), TOTAL_SUPPLY, 10000) 28 | Notify(["11_check", 10000]) 29 | return Get(GetContext(), TOTAL_SUPPLY) 30 | 31 | 32 | def receiveONG(account, ongAmount): 33 | Notify(["111_receiveONG"]) 34 | # Require(_transferONG(account, selfContractAddr_, ongAmount)) 35 | res = _transferONG(account, selfContractAddr_, ongAmount) 36 | if res == False: 37 | Notify(["receiveONG failed!"]) 38 | return False 39 | else: 40 | Notify(["receiveONG success!"]) 41 | return True 42 | 43 | 44 | def _transferONG(fromAcct, toAcct, amount): 45 | """ 46 | transfer amount of ONG from fromAcct to toAcct 47 | :param fromAcct: 48 | :param toAcct: 49 | :param amount: 50 | :return: 51 | """ 52 | # if CheckWitness(fromAcct): 53 | param = state(fromAcct, toAcct, amount) 54 | res = Invoke(0, ONGContractAddress_, 'transfer', [param]) 55 | if res and res == b'\x01': 56 | return True 57 | else: 58 | return False 59 | # else: 60 | # return False -------------------------------------------------------------------------------- /NativeAssetInvoke/native_asset_invoke.py: -------------------------------------------------------------------------------- 1 | from boa.interop.Ontology.Native import Invoke 2 | from boa.builtins import ToScriptHash, state 3 | from boa.interop.System.Runtime import Notify 4 | from boa.interop.System.ExecutionEngine import GetExecutingScriptHash 5 | 6 | 7 | # ONT Big endian Script Hash: 0x0100000000000000000000000000000000000000 8 | OntContract = ToScriptHash("AFmseVrdL9f9oyCzZefL9tG6UbvhUMqNMV") 9 | # ONG Big endian Script Hash: 0x0200000000000000000000000000000000000000 10 | OngContract = ToScriptHash("AFmseVrdL9f9oyCzZefL9tG6UbvhfRZMHJ") 11 | 12 | 13 | selfContractAddress = GetExecutingScriptHash() 14 | 15 | def Main(operation, args): 16 | if operation == "transferOntOng": 17 | if len(args) == 4: 18 | fromAcct = args[0] 19 | toAcct = args[1] 20 | ontAmount = args[2] 21 | ongAmount = args[3] 22 | return transferOntOng(fromAcct, toAcct, ontAmount, ongAmount) 23 | else: 24 | return False 25 | if operation == "transferOngToContract": 26 | if len(args) == 2: 27 | fromAccount = args[0] 28 | ongAmount = args[1] 29 | return transferOngToContract(fromAccount, ongAmount) 30 | else: 31 | return False 32 | if operation == "checkSelfContractONGAmount": 33 | return checkSelfContractONGAmount() 34 | return False 35 | 36 | def transferOntOng(fromAcct, toAcct, ontAmount, ongAmount): 37 | param = state(fromAcct, toAcct, ontAmount) 38 | res = Invoke(0, OntContract, "transfer", [param]) 39 | if res != b'\x01': 40 | raise Exception("transfer ont error.") 41 | param = state(fromAcct, toAcct, ongAmount) 42 | Notify("transferONT succeed") 43 | res = Invoke(0, OngContract, "transfer", [param]) 44 | if res != b'\x01': 45 | raise Exception("transfer ong error.") 46 | Notify("transferONG succeed") 47 | return True 48 | 49 | 50 | def transferOngToContract(fromAccount, ongAmount): 51 | Notify(["111_transferOngToContract", selfContractAddress]) 52 | param = state(fromAccount, selfContractAddress, ongAmount) 53 | res = Invoke(0, OngContract, 'transfer', [param]) 54 | if res and res == b'\x01': 55 | Notify('transfer Ong succeed') 56 | return True 57 | else: 58 | Notify('transfer Ong failed') 59 | return False 60 | 61 | 62 | def checkSelfContractONGAmount(): 63 | param = state(selfContractAddress) 64 | # do not use [param] 65 | res = Invoke(0, OngContract, 'balanceOf', param) 66 | return res -------------------------------------------------------------------------------- /Static_Call_Oep4/static_call_Oep4.py: -------------------------------------------------------------------------------- 1 | from boa.interop.System.App import RegisterAppCall 2 | from boa.interop.System.Runtime import Log 3 | from boa.interop.System.ExecutionEngine import GetExecutingScriptHash 4 | 5 | # Here "8ef4b22b006b49a85f5a9a4fe4cd42ce1ab809f4" should your OPE4 contract hash, pls note it's not reversed hash 6 | OEP4Contract = RegisterAppCall('8ef4b22b006b49a85f5a9a4fe4cd42ce1ab809f4', 'operation', 'args') 7 | 8 | selfContractAddress = GetExecutingScriptHash() 9 | 10 | def Main(operation, args): 11 | 12 | # Here you can define the method name "checkName" to anything you want 13 | if operation == "checkName": 14 | return checkName() 15 | # Here you can define the method name "checkBalanceOf" to anything you want 16 | if operation == "checkBalanceOf": 17 | if len(args) == 1: 18 | account = args[0] 19 | return checkBalanceOf(account) 20 | else: 21 | return False 22 | if operation == "checkSelfBalance": 23 | return checkSelfBalance() 24 | if operation == "checkTransfer": 25 | if len(args) != 3: 26 | Log("len(args)!=3 ") 27 | return False 28 | else: 29 | fromAcct = args[0] 30 | toAcct = args[1] 31 | tokenAmount = args[2] 32 | return checkTransfer(fromAcct, toAcct, tokenAmount) 33 | if operation == "sendOEP4TokenFromContractTo": 34 | if len(args) == 2: 35 | toAcct = args[0] 36 | tokenAmount = args[1] 37 | return sendOEP4TokenFromContractTo(toAcct, tokenAmount) 38 | else: 39 | return False 40 | return False 41 | 42 | 43 | def checkName(): 44 | # This "name" below should be consistent with your OEP4Contract methods 45 | # return OEP4Contract("name") is wrong 46 | # return OEP4Contract("name", []) or return OEP4Contract("name", 0) is correct! 47 | return OEP4Contract("name", 0) 48 | 49 | 50 | def checkBalanceOf(account): 51 | # This "balanceOf" below should be consistent with your OEP4Contract methods 52 | # params = account is wrong 53 | params = [account] 54 | return OEP4Contract("balanceOf", params) 55 | 56 | def checkSelfBalance(): 57 | params = [selfContractAddress] 58 | return OEP4Contract("balanceOf", params) 59 | 60 | def checkTransfer(fromAcct, toAcct, tokenAmount): 61 | params = [fromAcct, toAcct, tokenAmount] 62 | return OEP4Contract("transfer", params) 63 | 64 | def sendOEP4TokenFromContractTo(toAcct, tokenAmount): 65 | params = [selfContractAddress, toAcct, tokenAmount] 66 | return OEP4Contract("transfer", params) 67 | -------------------------------------------------------------------------------- /DynamicCallContract/README.md: -------------------------------------------------------------------------------- 1 | ```DynamicCallContract.py``` with script hash 2c2e3ab6a13134c6b2cf92e34d458c98332807f6 2 | ```CheckContract.py``` with script hash b16e976491982ddccd195dd73bd952a423a5e833 3 | 4 | Please note that the first parameter of ```DynamicAppCall``` method should be the reversed contract script hash. 5 | 6 | ```ontology contract invoke --address=2c2e3ab6a13134c6b2cf92e34d458c98332807f6 --params=string:StaticCallContract,[string:check,[int:0]] --gaslimit=200000 --gasprice=500``` 7 | or ```ontology contract invoke --address=2c2e3ab6a13134c6b2cf92e34d458c98332807f6 --params=string:StaticCallContract,[string:check,int:0] --gaslimit=200000 --gasprice=500``` 8 | 9 | coresponds with 10 | 11 | ```angular2html 12 | { 13 | "TxHash": "7c102f8000f19d4abb3f8721bac402c8d126089a738da413da6599fec9c72737", 14 | "State": 1, 15 | "GasConsumed": 10000000, 16 | "Notify": [ 17 | { 18 | "ContractAddress": "b16e976491982ddccd195dd73bd952a423a5e833", 19 | "States": [ 20 | "31315f636865636b", //11_check 21 | "1027" //10000 22 | ] 23 | }, 24 | { 25 | "ContractAddress": "0200000000000000000000000000000000000000", 26 | "States": [ 27 | "transfer", 28 | "AQf4Mzu1YJrhz9f3aRkkwSm9n3qhXGSh4p", 29 | "AFmseVrdL9f9oyCzZefL9tG6UbviEH9ugK", 30 | 10000000 31 | ] 32 | } 33 | ] 34 | } 35 | ``` 36 | 37 | ```ontology contract invoke --address=2c2e3ab6a13134c6b2cf92e34d458c98332807f6 --params=string:DynamicCallContract,[bytearray:33e8a523a452d93bd75d19cddc2d989164976eb1,string:check,int:0] --gaslimit=200000 --gasprice=500``` 38 | 39 | coresponds with 40 | 41 | ```angular2html 42 | { 43 | "TxHash": "fe87d192de5c5d3725150dafb91c2b787617c63b938e1b11f1366a87af09e427", 44 | "State": 1, 45 | "GasConsumed": 10000000, 46 | "Notify": [ 47 | { 48 | "ContractAddress": "b16e976491982ddccd195dd73bd952a423a5e833", 49 | "States": [ 50 | "31315f636865636b", //11_check 51 | "1027" //10000 52 | ] 53 | }, 54 | { 55 | "ContractAddress": "2c2e3ab6a13134c6b2cf92e34d458c98332807f6", 56 | "States": [ 57 | "6279746561727261793a20", //bytearray: 58 | "33e8a523a452d93bd75d19cddc2d989164976eb1" 59 | ] 60 | }, 61 | { 62 | "ContractAddress": "2c2e3ab6a13134c6b2cf92e34d458c98332807f6", 63 | "States": [ 64 | "3131315f44796e616d696343616c6c", // 111_DynamicCall 65 | "33e8a523a452d93bd75d19cddc2d989164976eb1", 66 | "636865636b", //check 67 | "00" //0 68 | ] 69 | }, 70 | { 71 | "ContractAddress": "2c2e3ab6a13134c6b2cf92e34d458c98332807f6", 72 | "States": [ 73 | "3232325f44796e616d696343616c6c",//222_DynamicCall 74 | "1027" //10000 75 | ] 76 | }, 77 | { 78 | "ContractAddress": "0200000000000000000000000000000000000000", 79 | "States": [ 80 | "transfer", 81 | "AQf4Mzu1YJrhz9f3aRkkwSm9n3qhXGSh4p", 82 | "AFmseVrdL9f9oyCzZefL9tG6UbviEH9ugK", 83 | 10000000 84 | ] 85 | } 86 | ] 87 | } 88 | ``` 89 | 90 | -------------------------------------------------------------------------------- /Static_Call_Oep4/static_call_Oep4_compiler2.0.py: -------------------------------------------------------------------------------- 1 | OntCversion = '2.0.0' 2 | from ontology.interop.System.App import RegisterAppCall, DynamicAppCall 3 | from ontology.interop.System.Runtime import Log, Notify 4 | from ontology.interop.System.ExecutionEngine import GetExecutingScriptHash 5 | 6 | # Here "8ef4b22b006b49a85f5a9a4fe4cd42ce1ab809f4" should your OPE4 contract hash, pls note it's not reversed hash 7 | OEP4Contract = RegisterAppCall('8ef4b22b006b49a85f5a9a4fe4cd42ce1ab809f4', 'operation', 'args') 8 | 9 | selfContractAddress = GetExecutingScriptHash() 10 | 11 | 12 | def Main(operation, args): 13 | # Here you can define the method name "checkName" to anything you want 14 | if operation == "checkName": 15 | return checkName() 16 | # Here you can define the method name "checkBalanceOf" to anything you want 17 | if operation == "checkBalanceOf": 18 | if len(args) == 1: 19 | account = args[0] 20 | return checkBalanceOf(account) 21 | else: 22 | return False 23 | if operation == "checkSelfBalance": 24 | return checkSelfBalance() 25 | if operation == "checkTransfer": 26 | if len(args) != 3: 27 | Log("len(args)!=3 ") 28 | return False 29 | else: 30 | fromAcct = args[0] 31 | toAcct = args[1] 32 | tokenAmount = args[2] 33 | return checkTransfer(fromAcct, toAcct, tokenAmount) 34 | if operation == "sendOEP4TokenFromContractTo": 35 | if len(args) == 2: 36 | toAcct = args[0] 37 | tokenAmount = args[1] 38 | return sendOEP4TokenFromContractTo(toAcct, tokenAmount) 39 | else: 40 | return False 41 | if operation == "DynamicSendToken": 42 | revesedContractAddress = args[0] 43 | operation = args[1] 44 | params = args[2] 45 | return DynamicSendToken(revesedContractAddress, operation, params) 46 | return False 47 | 48 | 49 | def checkName(): 50 | # This "name" below should be consistent with your OEP4Contract methods 51 | # return OEP4Contract("name") is wrong 52 | # return OEP4Contract("name", []) or return OEP4Contract("name", 0) is correct! 53 | return OEP4Contract("name", 0) 54 | 55 | 56 | def checkBalanceOf(account): 57 | # This "balanceOf" below should be consistent with your OEP4Contract methods 58 | # params = account is wrong 59 | params = [account] 60 | return OEP4Contract("balanceOf", params) 61 | 62 | 63 | def checkSelfBalance(): 64 | params = [selfContractAddress] 65 | return OEP4Contract("balanceOf", params) 66 | 67 | 68 | def checkTransfer(fromAcct, toAcct, tokenAmount): 69 | params = [fromAcct, toAcct, tokenAmount] 70 | return OEP4Contract("transfer", params) 71 | 72 | 73 | def sendOEP4TokenFromContractTo(toAcct, tokenAmount): 74 | params = [selfContractAddress, toAcct, tokenAmount] 75 | return OEP4Contract("transfer", params) 76 | 77 | def DynamicSendToken(revesedContractAddress, operation, params): 78 | Notify(["bytearray: ", revesedContractAddress]) 79 | res = DynamicAppCall(revesedContractAddress, operation, params) 80 | Notify(["111_DynamicSendToken", revesedContractAddress, operation, params]) 81 | Notify(["222_DynamicSendToken", res]) 82 | return res 83 | 84 | -------------------------------------------------------------------------------- /NativeAssetInvoke/native_asset_invoke_compiler2.0.py: -------------------------------------------------------------------------------- 1 | OntCversion = '2.0.0' 2 | from ontology.interop.Ontology.Native import Invoke 3 | from ontology.builtins import state 4 | from ontology.interop.System.Runtime import Notify 5 | from ontology.interop.System.ExecutionEngine import GetExecutingScriptHash 6 | from ontology.interop.Ontology.Runtime import Base58ToAddress 7 | 8 | # ONT Big endian Script Hash: 0x0100000000000000000000000000000000000000 9 | OntContract = Base58ToAddress("AFmseVrdL9f9oyCzZefL9tG6UbvhUMqNMV") 10 | # ONG Big endian Script Hash: 0x0200000000000000000000000000000000000000 11 | OngContract = Base58ToAddress("AFmseVrdL9f9oyCzZefL9tG6UbvhfRZMHJ") 12 | 13 | 14 | def Main(operation, args): 15 | if operation == "transferOntOng": 16 | if len(args) != 4: 17 | Notify("wrong params") 18 | return 19 | return transferOntOng(args[0], args[1], args[2], args[3]) 20 | if operation == "transferOngToContract": 21 | return transferOngToContract(args[0], args[1]) 22 | if operation == "balanceOf1": 23 | return balanceOf1(args[0]) 24 | if operation == "balanceOf2": 25 | return balanceOf2() 26 | 27 | return False 28 | 29 | def transferOntOng(from_acct, to_acct, ont, ong): 30 | param = state(from_acct, to_acct, ont) 31 | res = Invoke(0, OntContract, "transfer", [param]) 32 | if res != b'\x01': 33 | raise Exception("transfer ont error.") 34 | param = state(from_acct, to_acct, ong) 35 | Notify("transferONT succeed") 36 | res = Invoke(0, OngContract, "transfer", [param]) 37 | 38 | if res and res == b'\x01': 39 | Notify('transfer succeed') 40 | return True 41 | else: 42 | Notify('transfer failed') 43 | 44 | return False 45 | 46 | 47 | def transferOngToContract(account, ongAmount): 48 | selfContractAddress = GetExecutingScriptHash() 49 | Notify(["111_transferOngToContract", selfContractAddress]) 50 | param = state(account, selfContractAddress, ongAmount) 51 | ongContractAddress = ToScriptHash("AFmseVrdL9f9oyCzZefL9tG6UbvhfRZMHJ") 52 | res = Invoke(0, ongContractAddress, 'transfer', [param]) 53 | if res and res == b'\x01': 54 | Notify('transfer succeed') 55 | return True 56 | else: 57 | Notify('transfer failed') 58 | 59 | return False 60 | 61 | def balanceOf1(acct): 62 | """ 63 | transfer ONG from contract 64 | :param acct: 65 | :return: 66 | """ 67 | # return ongContract("balanceOf", [acct]) 68 | # return True 69 | 70 | # ONT native contract address 71 | # contractAddress = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') 72 | contractAddress = ToScriptHash("AFmseVrdL9f9oyCzZefL9tG6UbvhfRZMHJ") 73 | param = state(acct) 74 | res = Invoke(0, contractAddress, 'balanceOf', acct) 75 | return res 76 | 77 | 78 | def balanceOf2(): 79 | """ 80 | transfer ONG from contract 81 | :param acct: 82 | :return: 83 | """ 84 | # return ongContract("balanceOf", [acct]) 85 | # return True 86 | 87 | # ONT native contract address 88 | # contractAddress = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') 89 | ongContractAddress = ToScriptHash("AFmseVrdL9f9oyCzZefL9tG6UbvhfRZMHJ") 90 | param = state(GetExecutingScriptHash()) 91 | res = Invoke(0, ongContractAddress, 'balanceOf', param) 92 | 93 | # res = ONGContract("balanceOf", [acct]) 94 | 95 | return res -------------------------------------------------------------------------------- /ListandMapSample/README.md: -------------------------------------------------------------------------------- 1 | ```listAndMapSample.py``` has the script hash of ```f8868fb3dca112511494da07bc18d51711094e55```. 2 | 3 | 4 | ```ontology contract invoke --address=f8868fb3dca112511494da07bc18d51711094e55 --params=string:init,[int:0] --gaslimit=200000 --gasprice=500``` 5 | 6 | coresponds with 7 | 8 | ```angular2html 9 | { 10 | "TxHash": "0d528f9c6283213ad992eb290a3681942da6eab66504385d8eda1d3aa8fc4328", 11 | "State": 1, 12 | "GasConsumed": 10000000, 13 | "Notify": [ 14 | { 15 | "ContractAddress": "f8868fb3dca112511494da07bc18d51711094e55", 16 | "States": [ 17 | "696e6974206c69737420697320", //init list is 18 | [ 19 | "01", 20 | "02", 21 | "03" 22 | ] 23 | ] 24 | }, 25 | { 26 | "ContractAddress": "f8868fb3dca112511494da07bc18d51711094e55", 27 | "States": [ 28 | "696e6974206d617020697320", // init map is 29 | "01", 30 | "02" 31 | ] 32 | }, 33 | { 34 | "ContractAddress": "0200000000000000000000000000000000000000", 35 | "States": [ 36 | "transfer", 37 | "AQf4Mzu1YJrhz9f3aRkkwSm9n3qhXGSh4p", 38 | "AFmseVrdL9f9oyCzZefL9tG6UbviEH9ugK", 39 | 10000000 40 | ] 41 | } 42 | ] 43 | } 44 | ``` 45 | 46 | ```ontology contract invoke --address=f8868fb3dca112511494da07bc18d51711094e55 --params=string:addList,[[int:0,int:10,int:100,int:1000]] --gaslimit=200000 --gasprice=500``` 47 | 48 | coresponds with 49 | 50 | ```angular2html 51 | { 52 | "TxHash": "1885fb9f5403574b5edb0b1e6066593e555d2a6aa30f0bb64f71c65d729bc5e0", 53 | "State": 1, 54 | "GasConsumed": 10000000, 55 | "Notify": [ 56 | { 57 | "ContractAddress": "f8868fb3dca112511494da07bc18d51711094e55", 58 | "States": [ 59 | "6265666f7265206164642c206c69737420697320", //before add, list is 60 | [ 61 | "01", 62 | "02", 63 | "03" 64 | ] 65 | ] 66 | }, 67 | { 68 | "ContractAddress": "f8868fb3dca112511494da07bc18d51711094e55", 69 | "States": [ 70 | "6166746572206164642c206c69737420697320", //after add, list is 71 | [ 72 | "01", 73 | "02", 74 | "03", 75 | "00", 76 | "0a", 77 | "64", 78 | "e803" 79 | ] 80 | ] 81 | }, 82 | { 83 | "ContractAddress": "0200000000000000000000000000000000000000", 84 | "States": [ 85 | "transfer", 86 | "AQf4Mzu1YJrhz9f3aRkkwSm9n3qhXGSh4p", 87 | "AFmseVrdL9f9oyCzZefL9tG6UbviEH9ugK", 88 | 10000000 89 | ] 90 | } 91 | ] 92 | } 93 | ``` 94 | ```ontology contract invoke --address=f8868fb3dca112511494da07bc18d51711094e55 --params=string:addMap,[string:key3,int:99] --gaslimit=200000 --gasprice=500``` 95 | 96 | coresponds with 97 | 98 | ```angular2html 99 | { 100 | "TxHash": "31ed89da653f50d7f8d10a53b2673b00d52ccb33d20fcf66150e5d0a451ca8f7", 101 | "State": 1, 102 | "GasConsumed": 10000000, 103 | "Notify": [ 104 | { 105 | "ContractAddress": "f8868fb3dca112511494da07bc18d51711094e55", 106 | "States": [ 107 | "6265666f7265206164642c206d617020697320", //before add, map is 108 | "01", 109 | "02" 110 | ] 111 | }, 112 | { 113 | "ContractAddress": "f8868fb3dca112511494da07bc18d51711094e55", 114 | "States": [ 115 | "6166746572206164642c206d617020697320", //after add, map is 116 | "02", 117 | "63" //99 118 | ] 119 | }, 120 | { 121 | "ContractAddress": "0200000000000000000000000000000000000000", 122 | "States": [ 123 | "transfer", 124 | "AQf4Mzu1YJrhz9f3aRkkwSm9n3qhXGSh4p", 125 | "AFmseVrdL9f9oyCzZefL9tG6UbviEH9ugK", 126 | 10000000 127 | ] 128 | } 129 | ] 130 | } 131 | ``` -------------------------------------------------------------------------------- /OEP35Sample/README.md: -------------------------------------------------------------------------------- 1 | # TokenProxy Introduction 2 | 3 | ## 1. Overview 4 | 5 | 6 | OEP-35 is a standard cryto token exchange protocol for DDXF, it divides the exchange platform into two component`Exchange` and `TokenProxy` 7 | 8 | **Exchange** is responsible for combining Maker's and Taker's orders, and sending orders to TokenProxy smart contrat. 9 | 10 | **TokenProxy** is designed to decouple orders matching and token settlement on the blockchain, the token include ONT,ONG and other OEPs Token(like OEP-4,OEP-5,OEP-8) 11 | 12 | ## 2. Arichitecture 13 | 14 | ![DDXF-ARCH](https://ws4.sinaimg.cn/large/006tNc79gy1fyzbzs2itbj30mi0gz3zc.jpg) 15 | 16 | ## 3. Exchange 17 | 18 | ### 3.1 Order Message Format 19 | 20 | | Field | Type | Description | 21 | | ---------------- | ------- | -------------------------------------------- | 22 | | version | int | order version | 23 | | makerAddress | address | maker wallet address | 24 | | makerAssetAmount | int | Amount of makerAsset being offered by maker. | 25 | | makerTokenHash | bytes | the token hash of offered by maker | 26 | | takerAddress | address | taker wallet address | 27 | | takerAssetAmount | int | Amount of takerAsset being bid on by maker. | 28 | | takerTokenHash | bytes | the token hash of offered by maker | 29 | | makerFee | int | Amount of fee paid to feeReceiver by maker | 30 | | takerFee | int | Amount of fee paid to feeReceiver by taker | 31 | | feeTokenHash | bytes | Exchange-charged token hash | 32 | | feeReceiver | int | exchange fee receiver | 33 | | expireTime | int | order expire time | 34 | 35 | ## 4. TokenProxy Interface 36 | 37 | ### 4.1 Method 38 | 39 | #### 4.1.1 AddToken 40 | 41 | ```python 42 | def AddToken(symbol:string, contractHash:bytearray) 43 | ``` 44 | - `symbol` is token symbol, like "ONT", "ONG" 45 | - `contractHash` token script hash,such as ONT,ONG or other token hash, if success, this token can be exchanged on the exchange. 46 | 47 | **Invoked only by smart contract admin** 48 | 49 | #### 4.1.2 RemoveToken 50 | 51 | ```python 52 | def RemoveToken(symbol:string, contractHash:bytearray) 53 | ``` 54 | - `symbol` is token symbol, like "ONT", "ONG" 55 | - contractHash` This token will be removed from the exchange. 56 | 57 | **Invoked only by smart contract admin** 58 | 59 | #### 4.1.3 GetTokens 60 | 61 | ```python 62 | def GetTokens() 63 | ``` 64 | - Get all the tokens that can be traded in the exchange, it will response a map data structure. 65 | 66 | #### 4.1.4 RegisterExchange 67 | 68 | ```python 69 | def RegisterExchange(exchangeName:string, exchangeId:bytearray) 70 | ``` 71 | - `exchangeName` is exchange name, like "Huobi" 72 | - exchangeId` is exchange Id, essentially it is also a wallet address, only this exchange account can invoke token proxy smart contract 73 | 74 | #### 4.1.5 RemoveExchange 75 | 76 | ```python 77 | def RemoveExchange(exchangeId:bytearray) 78 | ``` 79 | - `exchangeName` is exchange name, like "Huobi" 80 | - exchangeId` is exchange Id,if success, the exchange can't invoke token proxy smart contract. 81 | 82 | **Invoked only by smart contract admin** 83 | 84 | #### 4.1.6 GetAuthorizedExchange 85 | 86 | ```python 87 | def GetAuthorizedExchange() 88 | ``` 89 | - Get all the exchange list that can invoke token proxy smart contract. 90 | 91 | #### 4.1.7 TokenTransferFrom 92 | 93 | ```python 94 | def TokenTransferFrom(exchangeId:bytearray, order:Order) 95 | ``` 96 | - `exchangerId`exchange id, **Invoked only by registered exchange** 97 | - `order` base on [Order Message Format](### 3.1 Order-Message-Format) 98 | 99 | This function will finish the token settlement on blockchain according the order message. 100 | 101 | **Note:**The user needs to approve the tokens to token proxy smart contract in advance. 102 | 103 | 104 | #### 4.1.8 TokenTransferFromMulti 105 | 106 | ```python 107 | def TokenTransferFromMulti(transferList:bytearray) 108 | ``` 109 | `transferList` is` TokenTransferFrom` function parameter array list, 110 | 111 | **Invoked only by registered exchange** 112 | 113 | 114 | 115 | ### 4.2 Event 116 | 117 | #### 4.2.1. AddToken 118 | 119 | ```python 120 | AddToken(contractHash:bytearray) 121 | ``` 122 | 123 | #### 4.2.2. RemoveToken 124 | 125 | ```python 126 | RemoveToken(contractHash:bytearray) 127 | ``` 128 | 129 | #### 4.2.3. RegisterExchange 130 | 131 | ```python 132 | RegisterExchange(exchangeId:bytearray) 133 | ``` 134 | 135 | #### 4.2.4. RemoveExchange 136 | 137 | ```python 138 | RemoveExchange(exchangeId:bytearray) 139 | ``` 140 | 141 | #### 4.2.5. TokenTransferFrom 142 | 143 | ```python 144 | TokenTransferFrom(order:Order) 145 | ``` 146 | -------------------------------------------------------------------------------- /ExecutingEntryCallingScriptHash/README.md: -------------------------------------------------------------------------------- 1 | The usage api can be found [here](https://apidoc.ont.io/smartcontract/#executionengine-entryscripthash). 2 | 3 | ContractB script hash: 4d99d1dad2caf811c3452fc1ef9cc1a3a8b59b9a 4 | 5 | ContractA script hash: b0339f7bea0b2a38ab7294bd41106d354a6b489a 6 | 7 | 8 | When we invoke ```invokeA``` method within contractA.py through Cli using 9 | ```ontology contract invoke --address=b0339f7bea0b2a38ab7294bd41106d354a6b489a --params=string:invokeA,[string:invokeB,[int:999]] --gaslimit=200000 --gasprice=500```, 10 | we get 11 | 12 | ``` 13 | { 14 | "TxHash": "b8268ff89abcf9e39f531a9c7ce569aaab226eb21dc41f1e1b851d1fe600234d", 15 | "State": 1, 16 | "GasConsumed": 10000000, 17 | "Notify": [ 18 | { 19 | "ContractAddress": "4d99d1dad2caf811c3452fc1ef9cc1a3a8b59b9a", 20 | "States": [ 21 | "3131315f696e766f6b6542", //111_invokeB 22 | "e703" //999 23 | ] 24 | }, 25 | { 26 | "ContractAddress": "4d99d1dad2caf811c3452fc1ef9cc1a3a8b59b9a", 27 | "States": [ 28 | "9a486b4a356d1041bd9472ab382a0bea7b9f33b0", 29 | "0054bb8e669a8314b46dcd5d3144e0ed631004a9", 30 | "9a9bb5a8a3c19cefc12f45c311f8cad2dad1994d" 31 | ] 32 | }, 33 | { 34 | "ContractAddress": "b0339f7bea0b2a38ab7294bd41106d354a6b489a", 35 | "States": [ 36 | "3131315f696e766f6b6541", // 111_invokeA 37 | "0054bb8e669a8314b46dcd5d3144e0ed631004a9", 38 | "0054bb8e669a8314b46dcd5d3144e0ed631004a9", 39 | "9a486b4a356d1041bd9472ab382a0bea7b9f33b0" 40 | ] 41 | }, 42 | { 43 | "ContractAddress": "0200000000000000000000000000000000000000", 44 | "States": [ 45 | "transfer", 46 | "AQf4Mzu1YJrhz9f3aRkkwSm9n3qhXGSh4p", 47 | "AFmseVrdL9f9oyCzZefL9tG6UbviEH9ugK", 48 | 10000000 49 | ] 50 | } 51 | ] 52 | } 53 | ``` 54 | 55 | When we invoke ```invokeA``` method within contractA.py through Cli using 56 | ```ontology contract invoke --address=b0339f7bea0b2a38ab7294bd41106d354a6b489a --params=string:invokeA,[string:avoidToBeInvokedByContract,[int:0]] --gaslimit=200000 --gasprice=500```, 57 | 58 | we get 59 | 60 | ``` 61 | { 62 | "TxHash": "6d963d17ac3a2f4ffacbd333306529f1c86cfa1d6c6e924a11b46e2035e7c483", 63 | "State": 1, 64 | "GasConsumed": 10000000, 65 | "Notify": [ 66 | { 67 | "ContractAddress": "4d99d1dad2caf811c3452fc1ef9cc1a3a8b59b9a", 68 | "States": [ 69 | "596f7520617265206e6f7420616c6c6f77656420746f20696e766f6b652074686973206d6574686f64207468726f75676820636f6e7472616374" 70 | //You are not allowed to invoke this method through contract 71 | ] 72 | }, 73 | { 74 | "ContractAddress": "b0339f7bea0b2a38ab7294bd41106d354a6b489a", 75 | "States": [ 76 | "3131315f696e766f6b6541", // 111_invokeA 77 | "c12ab6712d524d8bd21c895158737a9f8f0422e6", 78 | "c12ab6712d524d8bd21c895158737a9f8f0422e6", 79 | "9a486b4a356d1041bd9472ab382a0bea7b9f33b0" 80 | ] 81 | }, 82 | { 83 | "ContractAddress": "0200000000000000000000000000000000000000", 84 | "States": [ 85 | "transfer", 86 | "AQf4Mzu1YJrhz9f3aRkkwSm9n3qhXGSh4p", 87 | "AFmseVrdL9f9oyCzZefL9tG6UbviEH9ugK", 88 | 10000000 89 | ] 90 | } 91 | ] 92 | } 93 | ``` 94 | 95 | 96 | 97 | When we invoke ```checkHash``` method within contractA.pythrough Cli using 98 | ```ontology contract invoke --address=b0339f7bea0b2a38ab7294bd41106d354a6b489a --params=string:checkHash,[int:0] --gaslimit=200000 --gasprice=500```, 99 | we get 100 | 101 | 102 | ``` 103 | { 104 | "TxHash": "007ab582e85642a73687e9618a6148da9c38b54f03e76b094ed23917e7a86791", 105 | "State": 1, 106 | "GasConsumed": 10000000, 107 | "Notify": [ 108 | { 109 | "ContractAddress": "b0339f7bea0b2a38ab7294bd41106d354a6b489a", 110 | "States": [ 111 | "3131315f636865636b48617368" //111_checkHash 112 | ] 113 | }, 114 | { 115 | "ContractAddress": "b0339f7bea0b2a38ab7294bd41106d354a6b489a", 116 | "States": [ 117 | "796e304a6d5c6b195e553b36284ed519997d8bed", 118 | "796e304a6d5c6b195e553b36284ed519997d8bed", 119 | "9a486b4a356d1041bd9472ab382a0bea7b9f33b0" 120 | ] 121 | }, 122 | { 123 | "ContractAddress": "0200000000000000000000000000000000000000", 124 | "States": [ 125 | "transfer", 126 | "AQf4Mzu1YJrhz9f3aRkkwSm9n3qhXGSh4p", 127 | "AFmseVrdL9f9oyCzZefL9tG6UbviEH9ugK", 128 | 10000000 129 | ] 130 | } 131 | ] 132 | } 133 | ``` 134 | 135 | 136 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Disclaimer 2 | All downloads and use of this repository (python-template) are deemed to have been read carefully and fully agree to the following terms. 3 | 4 | * This repository is only for personal learning and communication purposes and is strictly prohibited for commercial and non-use purposes. 5 | * The authors have the right to revoke the right to use any commercial activity or bad use. 6 | * The risks of using the contents within this repository (python-template) will be entirely token by the users, and the repository authors will not hold any responsibility. 7 | * The repository authors are not responsible for any accidents, negligence, contract damage, defects, copyright or other intellectual property rights infringement and any damage caused by improper use of any contents within this repository (python-template). And the repository authors will not take any legal responsibility. 8 | * The repository (python-template) authors do not assume any responsibility for service interruption or other defects caused by force majeure or hacking attacks, communication line interruptions, etc. In addition, under the circumstances that the authors do not take any responsibility, the authors will try to reduce the loss or bad effects caused to the users. 9 | * For issues not covered by this statement, please refer to relevant national laws and regulations. When this statement conflicts with relevant national laws and regulations, the national laws and regulations shall prevail. 10 | * The copyright of this repository and its right of modification, renewal and final interpretation are owned by the authors of the repository (python-template). 11 | 12 | ## Introduction 13 | This project aims for providing demos in python version for developing smart contract based on ontology. 14 | The following templates have been tested smoothly. The template contracts all work well 15 | and they can be used as reference when you write ONT smart contract. 16 | 17 | * libs contain safe SafeMath, SafeCheck and Utils files 18 | * migrate_destroyWithinContract.py 19 | * static_call_Oep4.py 20 | * struct_example.py 21 | * storage_example.py 22 | * native_asset_invoke.py 23 | * OEP4Sample.py 24 | * OEP5Sample.py 25 | * OEP8Sample.py 26 | * event_test.py 27 | * Compare Executing Entry and Calling ScriptHash 28 | * Dynamic Call Contract 29 | * List and Map Sample 30 | * UnboundWithdrawONG 31 | 32 | ## Instruction 33 | 34 | ### Preparation 35 | 36 | ##### 1. configure ontology cli 37 | For test usage, you can start ontology node and test your contract under ontology cli. 38 | ###### 1.1 Install ontology cli 39 | The instructions for installation of cli is given [here](https://github.com/ontio/ontology). 40 | 41 | ###### 1.2 Usage of ontology cli 42 | The instructions for usage of cli is given here ([EN](https://github.com/ontio/ontology/blob/master/docs/specifications/cli_user_guide.md), [CN](https://github.com/ontio/ontology/blob/master/docs/specifications/cli_user_guide_CN.md)). 43 | 44 | 45 | 46 | ##### 2. configure neo-boa environment 47 | Note here that the official installation instruction is a little bit misunderstanding. 48 | Here I will give the full and correct installation guide. 49 | 50 | ##### 2.1. Download [neo-boa (ONT version)](https://github.com/ontio/neo-boa) 51 | 52 | ##### 2.2. Make a Python 3 virtual environment and activate it (in windows system) via: 53 | 54 | ``` 55 | python3 -m venv venv 56 | cd venv/bin 57 | activate 58 | ``` 59 | ##### 2.3. Then, install the requirements: 60 | 61 | ``` 62 | pip install -r requirements.txt 63 | ``` 64 | 65 | ##### 2.4. Installation requires a Python 3.6 or later environment. 66 | 67 | ``` 68 | pip install neo-boa 69 | ``` 70 | 71 | ##### 2.5. You can read the [docs](https://neo-boa.readthedocs.io/en/latest/) about how to use neo-boa 72 | 73 | Suggest you can ignore boa-test since some of the paths are configured wrongly. 74 | 75 | 76 | ### Usage 77 | 78 | Download the "python-template" zip folder and unzip it to any folder you create parallel with "boa" folder. 79 | ##### 3.1 Compile contract 80 | Open "compile_contract.py", make sure there is nothing wrong with compiling it. Then you can run this py file. 81 | Accordingly, the corresponding readable avm file will be in the corresponded folder. 82 | 83 | Copy your avm file into your "$GOPATH/src/github.com/ontio/ontology/*" (the folder containing your ontology and wallet). 84 | ##### 3.2 Start ontology node 85 | ``` 86 | ontology --testmode --gasprice=0 87 | ``` 88 | ##### 3.3 Deploy contract 89 | Deploy smart contract in testmode. Here --code="your avm file" 90 | ``` 91 | ./ontology contract deploy --name=xxx 92 | --code=xxx 93 | --author=xxx --desc=xxx --email=xxx --needstore --gaslimit=100000000 94 | 95 | ``` 96 | ##### 3.4 Invoke contract 97 | Invoke methods within your contract, please refer to the usage of ontology cli ([EN](https://github.com/ontio/ontology/blob/master/docs/specifications/cli_user_guide.md), [CN](https://github.com/ontio/ontology/blob/master/docs/specifications/cli_user_guide_CN.md)). 98 | 99 | -------------------------------------------------------------------------------- /UnboundWithdrawONG/unboundWithdrawONG.py: -------------------------------------------------------------------------------- 1 | from boa.interop.Ontology.Contract import Migrate 2 | from boa.interop.System.Storage import GetContext, Get, Put, Delete 3 | from boa.interop.System.Runtime import CheckWitness, GetTime, Notify, Serialize, Deserialize 4 | from boa.interop.System.ExecutionEngine import GetExecutingScriptHash, GetCallingScriptHash, GetEntryScriptHash 5 | from boa.interop.Ontology.Native import Invoke 6 | from boa.interop.Ontology.Runtime import GetCurrentBlockHash 7 | from boa.builtins import ToScriptHash, concat, state 8 | 9 | ContractAddress = GetExecutingScriptHash() 10 | # ONTAddress = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') 11 | # ONGAddress = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') 12 | 13 | ONTAddress = ToScriptHash("AFmseVrdL9f9oyCzZefL9tG6UbvhUMqNMV") 14 | ONGAddress = ToScriptHash("AFmseVrdL9f9oyCzZefL9tG6UbvhfRZMHJ") 15 | 16 | 17 | def Main(operation, args): 18 | if operation == "invest": 19 | fromAcct = args[0] 20 | ontAmount = args[1] 21 | return invest(fromAcct, ontAmount) 22 | if operation == "withdraw": 23 | toAcct = args[0] 24 | ontAmount = args[1] 25 | return withdraw(toAcct, ontAmount) 26 | if operation == "checkAllowance": 27 | account = args[0] 28 | return checkAllowance(account) 29 | if operation == "checkWithdraw": 30 | account = args[0] 31 | return checkWithdraw(account) 32 | if operation == "checkBalanceOf": 33 | ongFlag = args[0] 34 | account = args[1] 35 | return checkBalanceOf(ongFlag, account) 36 | if operation == "unboundOngBalance": 37 | return unboundOngBalance() 38 | if operation == "withdrawOng": 39 | toAcct = args[0] 40 | return withdrawOng(toAcct) 41 | return False 42 | 43 | 44 | def invest(fromAcct, ontAmount): 45 | transferONT(fromAcct, ContractAddress, ontAmount) 46 | return True 47 | 48 | 49 | def withdraw(toAcct, ontAmount): 50 | transferONT(ContractAddress, toAcct, ontAmount) 51 | return True 52 | 53 | 54 | def checkAllowance(account): 55 | # param = state(ONTAddress, account) 56 | # if ongFlag == 1: 57 | # Notify(["ongFlag", ongFlag]) 58 | # unboundOngAmount = Invoke(0, ONGAddress, 'allowance', param) 59 | # else: 60 | # Notify(["ongFlag", ongFlag]) 61 | # unboundOngAmount = Invoke(0, ONTAddress, 'allowance', param) 62 | # Notify(["checkAllowance", account, unboundOngAmount]) 63 | 64 | param = state(ONTAddress, account) 65 | unboundOngAmount = Invoke(0, ONGAddress, 'allowance', param) 66 | Notify(["checkAllowance", account, unboundOngAmount]) 67 | 68 | return unboundOngAmount 69 | 70 | 71 | def checkWithdraw(account): 72 | allowanceOng = checkAllowance(account) 73 | withdrawOngAmount = allowanceOng / 2 74 | params = state(account, ONTAddress, account, withdrawOngAmount) 75 | res = Invoke(0, ONGAddress, 'transferFrom', params) 76 | if res and res == b'\x01': 77 | Notify(["withdraw ong successful!"]) 78 | return True 79 | else: 80 | Notify(["withdraw ong failed!"]) 81 | return False 82 | 83 | 84 | def checkBalanceOf(ongFlag, account): 85 | param = state(ContractAddress) 86 | # do not use [param] 87 | res = Invoke(0, ONGAddress, 'balanceOf', param) 88 | Notify(["ContractAddress", ContractAddress, res]) 89 | 90 | param = state(account) 91 | if ongFlag == 1: 92 | Notify(["ongFlag", ongFlag]) 93 | res = Invoke(0, ONGAddress, 'balanceOf', param) 94 | else: 95 | Notify(["ongFlag", ongFlag]) 96 | res = Invoke(0, ONTAddress, 'balanceOf', param) 97 | Notify(["checkBalanceOf", account, res]) 98 | return res 99 | 100 | 101 | def unboundOngBalance(): 102 | return checkAllowance(ContractAddress) 103 | 104 | 105 | def withdrawOng(toAcct): 106 | param = state(ONTAddress, ContractAddress) 107 | unboundOngAmount = Invoke(0, ONGAddress, 'allowance', param) 108 | Notify(["unboundOngAmount", unboundOngAmount]) 109 | if unboundOngAmount > 0: 110 | unboundOngAmount = 147 111 | params = state(ContractAddress, ONTAddress, toAcct, unboundOngAmount) 112 | res = Invoke(0, ONGAddress, "transferFrom", params) 113 | if res and res == b'\x01': 114 | Notify(["withdraw ong successful!"]) 115 | return True 116 | else: 117 | Notify(["withdraw ong failed!"]) 118 | return False 119 | else: 120 | Notify(["Not enough unboundOngAmount", unboundOngAmount]) 121 | return False 122 | 123 | 124 | def transferONT(fromAcct, toAcct, ontAmount): 125 | """ 126 | transfer ONG 127 | :param fromacct: 128 | :param toacct: 129 | :param amount: 130 | :return: 131 | """ 132 | param = state(fromAcct, toAcct, ontAmount) 133 | res = Invoke(0, ONTAddress, 'transfer', [param]) 134 | if res and res == b'\x01': 135 | Notify(["transfer ONT successful!"]) 136 | return True 137 | else: 138 | Notify(["transfer ONT failed!"]) 139 | return False 140 | -------------------------------------------------------------------------------- /UnboundWithdrawONG/unboundWithdrawONG_compiler2.0.py: -------------------------------------------------------------------------------- 1 | OntOntCversion = '2.0.0' 2 | from ontology.interop.Ontology.Contract import Migrate 3 | from ontology.interop.System.Storage import GetContext, Get, Put, Delete 4 | from ontology.interop.System.Runtime import CheckWitness, GetTime, Notify, Serialize, Deserialize 5 | from ontology.interop.System.ExecutionEngine import GetExecutingScriptHash, GetCallingScriptHash, GetEntryScriptHash 6 | from ontology.interop.Ontology.Native import Invoke 7 | from ontology.interop.Ontology.Runtime import GetCurrentBlockHash 8 | from ontology.builtins import concat, state 9 | from ontology.interop.Ontology.Runtime import Base58ToAddress 10 | 11 | ContractAddress = GetExecutingScriptHash() 12 | # ONTAddress = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') 13 | # ONGAddress = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') 14 | 15 | ONTAddress = Base58ToAddress("AFmseVrdL9f9oyCzZefL9tG6UbvhUMqNMV") 16 | ONGAddress = Base58ToAddress("AFmseVrdL9f9oyCzZefL9tG6UbvhfRZMHJ") 17 | 18 | 19 | def Main(operation, args): 20 | if operation == "invest": 21 | fromAcct = args[0] 22 | ontAmount = args[1] 23 | return invest(fromAcct, ontAmount) 24 | if operation == "withdraw": 25 | toAcct = args[0] 26 | ontAmount = args[1] 27 | return withdraw(toAcct, ontAmount) 28 | if operation == "checkAllowance": 29 | account = args[0] 30 | return checkAllowance(account) 31 | if operation == "checkWithdraw": 32 | account = args[0] 33 | return checkWithdraw(account) 34 | if operation == "checkBalanceOf": 35 | ongFlag = args[0] 36 | account = args[1] 37 | return checkBalanceOf(ongFlag, account) 38 | if operation == "unboundOngBalance": 39 | return unboundOngBalance() 40 | if operation == "withdrawOng": 41 | toAcct = args[0] 42 | return withdrawOng(toAcct) 43 | return False 44 | 45 | 46 | def invest(fromAcct, ontAmount): 47 | transferONT(fromAcct, ContractAddress, ontAmount) 48 | return True 49 | 50 | 51 | def withdraw(toAcct, ontAmount): 52 | transferONT(ContractAddress, toAcct, ontAmount) 53 | return True 54 | 55 | 56 | def checkAllowance(account): 57 | # param = state(ONTAddress, account) 58 | # if ongFlag == 1: 59 | # Notify(["ongFlag", ongFlag]) 60 | # unboundOngAmount = Invoke(0, ONGAddress, 'allowance', param) 61 | # else: 62 | # Notify(["ongFlag", ongFlag]) 63 | # unboundOngAmount = Invoke(0, ONTAddress, 'allowance', param) 64 | # Notify(["checkAllowance", account, unboundOngAmount]) 65 | 66 | param = state(ONTAddress, account) 67 | unboundOngAmount = Invoke(0, ONGAddress, 'allowance', param) 68 | Notify(["checkAllowance", account, unboundOngAmount]) 69 | 70 | return unboundOngAmount 71 | 72 | 73 | def checkWithdraw(account): 74 | allowanceOng = checkAllowance(account) 75 | withdrawOngAmount = allowanceOng / 2 76 | params = state(account, ONTAddress, account, withdrawOngAmount) 77 | res = Invoke(0, ONGAddress, 'transferFrom', params) 78 | if res and res == b'\x01': 79 | Notify(["withdraw ong successful!"]) 80 | return True 81 | else: 82 | Notify(["withdraw ong failed!"]) 83 | return False 84 | 85 | 86 | def checkBalanceOf(ongFlag, account): 87 | param = state(ContractAddress) 88 | # do not use [param] 89 | res = Invoke(0, ONGAddress, 'balanceOf', param) 90 | Notify(["ContractAddress", ContractAddress, res]) 91 | 92 | param = state(account) 93 | if ongFlag == 1: 94 | Notify(["ongFlag", ongFlag]) 95 | res = Invoke(0, ONGAddress, 'balanceOf', param) 96 | else: 97 | Notify(["ongFlag", ongFlag]) 98 | res = Invoke(0, ONTAddress, 'balanceOf', param) 99 | Notify(["checkBalanceOf", account, res]) 100 | return res 101 | 102 | 103 | def unboundOngBalance(): 104 | return checkAllowance(ContractAddress) 105 | 106 | 107 | def withdrawOng(toAcct): 108 | param = state(ONTAddress, ContractAddress) 109 | unboundOngAmount = Invoke(0, ONGAddress, 'allowance', param) 110 | Notify(["unboundOngAmount", unboundOngAmount]) 111 | if unboundOngAmount > 0: 112 | unboundOngAmount = 147 113 | params = state(ContractAddress, ONTAddress, toAcct, unboundOngAmount) 114 | res = Invoke(0, ONGAddress, "transferFrom", params) 115 | if res and res == b'\x01': 116 | Notify(["withdraw ong successful!"]) 117 | return True 118 | else: 119 | Notify(["withdraw ong failed!"]) 120 | return False 121 | else: 122 | Notify(["Not enough unboundOngAmount", unboundOngAmount]) 123 | return False 124 | 125 | 126 | def transferONT(fromAcct, toAcct, ontAmount): 127 | """ 128 | transfer ONG 129 | :param fromacct: 130 | :param toacct: 131 | :param amount: 132 | :return: 133 | """ 134 | param = state(fromAcct, toAcct, ontAmount) 135 | res = Invoke(0, ONTAddress, 'transfer', [param]) 136 | if res and res == b'\x01': 137 | Notify(["transfer ONT successful!"]) 138 | return True 139 | else: 140 | Notify(["transfer ONT failed!"]) 141 | return False 142 | -------------------------------------------------------------------------------- /OEP5Sample/README.md: -------------------------------------------------------------------------------- 1 | This contract indicates non-fungible tokens, allows tokens on ONT to be convenient used by other applications. 2 | ##### Notes 3 | 4 | ###### 1. Global Variable 5 | 6 | These are only for testing usage, aims to check the contract works properly. You can modify them based on your needs. 7 | ``` 8 | # TOKEN_INDEX_PREFIX, INITED, and TOTAL_SUPPLY for testing usage only 9 | TOKEN_INDEX_PREFIX = 'Index' 10 | TOTAL_SUPPLY = 'TotalSupply' 11 | INITED = 'Initialized' 12 | ``` 13 | ###### 2. Optional operation in main 14 | 15 | In the main function, these operations are defined in order to check the methods within the contract more conveniently. 16 | ```angular2html 17 | ############ For testing usage only starts ############ 18 | if operation == 'init': 19 | return init() 20 | if operation == 'queryTokenByID': 21 | if len(args) != 1: 22 | return False 23 | tokenID = args[0] 24 | return queryTokenByID(tokenID) 25 | if operation == 'totalSupply': 26 | return totalSupply() 27 | if operation == "getApproved": 28 | if len(args) != 1: 29 | return False 30 | tokenID = args[0] 31 | return getApproved(tokenID) 32 | if operation == "queryTokenIDByIndex": 33 | if len(args) != 1: 34 | return False 35 | index = args[0] 36 | return queryTokenIDByIndex(index) 37 | ############ For testing usage only ends ############ 38 | 39 | 40 | ``` 41 | 42 | ###### 3. Optional operation defination 43 | 44 | This part defines some optional methods (operation) for testing convenience. You can write your unique token based on what you need. 45 | 46 | ```angular2html 47 | #################### For testing usage only starts ###################### 48 | 49 | def init(): 50 | ''' 51 | based on your requirements, initialize the tokens 52 | :return: 53 | ''' 54 | Notify(["111_init"]) 55 | if not Get(ctx, INITED) and CheckWitness(admin) == True: 56 | Put(ctx, INITED, 'TRUE') 57 | Put(ctx, TOTAL_SUPPLY, 0) 58 | tt = createMultiTokens() 59 | if tt == True: 60 | # adminBalance = Get(ctx, concatkey(OWNER_BALANCE_PREFIX, admin)) 61 | # Put(ctx, TOTAL_SUPPLY, adminBalance) 62 | # Notify(["222_init", adminBalance]) 63 | return True 64 | return False 65 | else: 66 | Notify(["222_init"]) 67 | 68 | return False 69 | 70 | 71 | def totalSupply(): 72 | return Get(ctx, TOTAL_SUPPLY) 73 | 74 | 75 | def queryTokenIDByIndex(idx): 76 | ''' 77 | query tokenid by index 78 | :param idx: 79 | :return: 80 | ''' 81 | tokenID = Get(ctx, concatkey(TOKEN_INDEX_PREFIX, idx)) 82 | Notify(["111_queryTokenIDByIndex", tokenID]) 83 | return 84 | 85 | 86 | def queryTokenByID(tokenID): 87 | ''' 88 | query token detail by tokenID 89 | :param tokenID: 90 | :return: 91 | ''' 92 | Notify(["111_queryTokenByID", tokenID, concatkey(TOKEN_ID_PREFIX, tokenID)]) 93 | token = Get(ctx, concatkey(TOKEN_ID_PREFIX, tokenID)) 94 | token_info = Deserialize(token) 95 | id = token_info['ID'] 96 | name = token_info['Name'] 97 | image = token_info['Image'] 98 | type = token_info['Type'] 99 | Notify(["111_token info: ", id, name, image, type]) 100 | return True 101 | 102 | 103 | def getApproved(tokenID): 104 | ''' 105 | get the approved address of the token 106 | :param tokenID: 107 | :return: 108 | ''' 109 | key = concatkey(APPROVE_PREFIX, tokenID) 110 | return Get(ctx, key) 111 | 112 | 113 | def createMultiTokens(): 114 | Notify(["111_createMultiTokens begins"]) 115 | 116 | a1 = {'Name': 'HEART A', 'Image': 'http://images.com/hearta.jpg'} 117 | a2 = {'Name': 'HEART 2', 'Image': 'http://images.com/heart2.jpg'} 118 | a3 = {'Name': 'HEART 3', 'Image': 'http://images.com/heart3.jpg'} 119 | a4 = {'Name': 'HEART 4', 'Image': 'http://images.com/heart4.jpg'} 120 | a5 = {'Name': 'HEART 5', 'Image': 'http://images.com/heart5.jpg'} 121 | a6 = {'Name': 'HEART 6', 'Image': 'http://images.com/heart6.jpg'} 122 | a7 = {'Name': 'HEART 7', 'Image': 'http://images.com/heart7.jpg'} 123 | a8 = {'Name': 'HEART 8', 'Image': 'http://images.com/heart8.jpg'} 124 | a9 = {'Name': 'HEART 9', 'Image': 'http://images.com/heart9.jpg'} 125 | a10 = {'Name': 'HEART 10', 'Image': 'http://images.com/heart10.jpg'} 126 | a11 = {'Name': 'HEART J', 'Image': 'http://images.com/heartj.jpg'} 127 | a12 = {'Name': 'HEART Q', 'Image': 'http://images.com/heartq.jpg'} 128 | a13 = {'Name': 'HEART K', 'Image': 'http://images.com/heartk.jpg'} 129 | 130 | cards = [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13] 131 | for card in cards: 132 | if createOneToken(card['Name'], card['Image'], 'CARD') != True: 133 | raise Exception('_createMultiToken failed') 134 | Notify(["222_createMultiTokens ends"]) 135 | return True 136 | 137 | 138 | def createOneToken(name, url, type): 139 | ''' 140 | create a new token 141 | :param name: 142 | :param url: 143 | :param type: 144 | :return: 145 | ''' 146 | Notify(["111_createOneToken begins"]) 147 | # generate tokenID 148 | timestamp = GetTime() 149 | totalSupply = Get(ctx, TOTAL_SUPPLY) 150 | newTotalSupply = totalSupply + 1 151 | Put(ctx, TOTAL_SUPPLY, newTotalSupply) 152 | tmp = concatkey(concatkey(selfAddr, timestamp), newTotalSupply) 153 | tokenID = sha256(tmp) 154 | # construct token map 155 | token = {'ID': tokenID, 'Name': name, 'Image': url, 'Type': type} 156 | Notify(["222_createOneToken", newTotalSupply, tokenID, concatkey(TOKEN_ID_PREFIX, tokenID)]) 157 | Put(ctx, concatkey(TOKEN_INDEX_PREFIX, newTotalSupply), tokenID) 158 | ownerKey = concatkey(OWNER_OF_TOKEN_PREFIX, tokenID) 159 | Put(ctx, ownerKey, admin) 160 | Put(ctx, concatkey(TOKEN_ID_PREFIX, tokenID), Serialize(token)) 161 | # add to adminBalance 162 | adminBalance = Get(ctx, concatkey(OWNER_BALANCE_PREFIX, admin)) 163 | Put(ctx, concatkey(OWNER_BALANCE_PREFIX, admin), adminBalance + 1) 164 | Notify(["333_createOneToken ends"]) 165 | return True 166 | #################### For testing usage only ends ###################### 167 | ``` -------------------------------------------------------------------------------- /OEP4Sample/OEP4Sample.py: -------------------------------------------------------------------------------- 1 | """ 2 | An Example of OEP-4 3 | """ 4 | from boa.interop.System.Storage import GetContext, Get, Put, Delete 5 | from boa.interop.System.Runtime import Notify, CheckWitness 6 | from boa.interop.System.Action import RegisterAction 7 | from boa.builtins import concat, ToScriptHash 8 | # from boa.interop.Ontology.Runtime import AddressToBase58, Base58ToAddress 9 | 10 | TransferEvent = RegisterAction("transfer", "from", "to", "amount") 11 | ApprovalEvent = RegisterAction("approval", "owner", "spender", "amount") 12 | 13 | ctx = GetContext() 14 | 15 | NAME = 'MyToken' 16 | SYMBOL = 'MYT' 17 | DECIMALS = 8 18 | FACTOR = 100000000 19 | OWNER = ToScriptHash("AQf4Mzu1YJrhz9f3aRkkwSm9n3qhXGSh4p") 20 | # OWNER = bytearray(b'\x61\x6f\x2a\x4a\x38\x39\x6f\xf2\x03\xea\x01\xe6\xc0\x70\xae\x42\x1b\xb8\xce\x2d') 21 | TOTAL_AMOUNT = 1000000000 22 | BALANCE_PREFIX = bytearray(b'\x01') 23 | APPROVE_PREFIX = b'\x02' 24 | SUPPLY_KEY = 'TotalSupply' 25 | 26 | 27 | def Main(operation, args): 28 | """ 29 | :param operation: 30 | :param args: 31 | :return: 32 | """ 33 | # 'init' has to be invokded first after deploying the contract to store the necessary and important info in the blockchain 34 | if operation == 'init': 35 | return init() 36 | if operation == 'name': 37 | return name() 38 | if operation == 'symbol': 39 | return symbol() 40 | if operation == 'decimals': 41 | return decimals() 42 | if operation == 'totalSupply': 43 | return totalSupply() 44 | if operation == 'balanceOf': 45 | if len(args) != 1: 46 | return False 47 | acct = args[0] 48 | return balanceOf(acct) 49 | if operation == 'transfer': 50 | if len(args) != 3: 51 | return False 52 | else: 53 | from_acct = args[0] 54 | to_acct = args[1] 55 | amount = args[2] 56 | return transfer(from_acct,to_acct,amount) 57 | if operation == 'transferMulti': 58 | return transferMulti(args) 59 | if operation == 'transferFrom': 60 | if len(args) != 4: 61 | return False 62 | spender = args[0] 63 | from_acct = args[1] 64 | to_acct = args[2] 65 | amount = args[3] 66 | return transferFrom(spender,from_acct,to_acct,amount) 67 | if operation == 'approve': 68 | if len(args) != 3: 69 | return False 70 | owner = args[0] 71 | spender = args[1] 72 | amount = args[2] 73 | return approve(owner,spender,amount) 74 | if operation == 'allowance': 75 | if len(args) != 2: 76 | return False 77 | owner = args[0] 78 | spender = args[1] 79 | return allowance(owner,spender) 80 | return False 81 | 82 | def init(): 83 | """ 84 | initialize the contract, put some important info into the storage in the blockchain 85 | :return: 86 | """ 87 | if len(OWNER) != 20: 88 | Notify(["Owner illegal!"]) 89 | return False 90 | if Get(ctx,SUPPLY_KEY): 91 | Notify("Already initialized!") 92 | return False 93 | else: 94 | total = TOTAL_AMOUNT * FACTOR 95 | Put(ctx,SUPPLY_KEY,total) 96 | Put(ctx,concat(BALANCE_PREFIX,OWNER),total) 97 | 98 | # Notify(["transfer", "", Base58ToAddress(OWNER), total]) 99 | # ownerBase58 = AddressToBase58(OWNER) 100 | TransferEvent("", OWNER, total) 101 | 102 | return True 103 | 104 | 105 | def name(): 106 | """ 107 | :return: name of the token 108 | """ 109 | return NAME 110 | 111 | 112 | def symbol(): 113 | """ 114 | :return: symbol of the token 115 | """ 116 | return SYMBOL 117 | 118 | 119 | def decimals(): 120 | """ 121 | :return: the decimals of the token 122 | """ 123 | return DECIMALS 124 | 125 | 126 | def totalSupply(): 127 | """ 128 | :return: the total supply of the token 129 | """ 130 | return Get(ctx, SUPPLY_KEY) 131 | 132 | 133 | def balanceOf(account): 134 | """ 135 | :param account: 136 | :return: the token balance of account 137 | """ 138 | if len(account) != 20: 139 | raise Exception("address length error") 140 | return Get(ctx,concat(BALANCE_PREFIX,account)) 141 | 142 | 143 | def transfer(from_acct,to_acct,amount): 144 | """ 145 | Transfer amount of tokens from from_acct to to_acct 146 | :param from_acct: the account from which the amount of tokens will be transferred 147 | :param to_acct: the account to which the amount of tokens will be transferred 148 | :param amount: the amount of the tokens to be transferred, >= 0 149 | :return: True means success, False or raising exception means failure. 150 | """ 151 | if len(to_acct) != 20 or len(from_acct) != 20: 152 | raise Exception("address length error") 153 | if CheckWitness(from_acct) == False or amount < 0: 154 | return False 155 | 156 | fromKey = concat(BALANCE_PREFIX,from_acct) 157 | fromBalance = Get(ctx,fromKey) 158 | if amount > fromBalance: 159 | return False 160 | if amount == fromBalance: 161 | Delete(ctx,fromKey) 162 | else: 163 | Put(ctx,fromKey,fromBalance - amount) 164 | 165 | toKey = concat(BALANCE_PREFIX,to_acct) 166 | toBalance = Get(ctx,toKey) 167 | Put(ctx,toKey,toBalance + amount) 168 | 169 | # Notify(["transfer", AddressToBase58(from_acct), AddressToBase58(to_acct), amount]) 170 | # TransferEvent(AddressToBase58(from_acct), AddressToBase58(to_acct), amount) 171 | TransferEvent(from_acct, to_acct, amount) 172 | 173 | return True 174 | 175 | 176 | def transferMulti(args): 177 | """ 178 | :param args: the parameter is an array, containing element like [from, to, amount] 179 | :return: True means success, False or raising exception means failure. 180 | """ 181 | for p in args: 182 | if len(p) != 3: 183 | # return False is wrong 184 | raise Exception("transferMulti params error.") 185 | if transfer(p[0], p[1], p[2]) == False: 186 | # return False is wrong since the previous transaction will be successful 187 | raise Exception("transferMulti failed.") 188 | return True 189 | 190 | 191 | def approve(owner,spender,amount): 192 | """ 193 | owner allow spender to spend amount of token from owner account 194 | Note here, the amount should be less than the balance of owner right now. 195 | :param owner: 196 | :param spender: 197 | :param amount: amount>=0 198 | :return: True means success, False or raising exception means failure. 199 | """ 200 | if len(spender) != 20 or len(owner) != 20: 201 | raise Exception("address length error") 202 | if CheckWitness(owner) == False: 203 | return False 204 | if amount > balanceOf(owner) or amount < 0: 205 | return False 206 | 207 | key = concat(concat(APPROVE_PREFIX,owner),spender) 208 | Put(ctx, key, amount) 209 | 210 | # Notify(["approval", AddressToBase58(owner), AddressToBase58(spender), amount]) 211 | # ApprovalEvent(AddressToBase58(owner), AddressToBase58(spender), amount) 212 | ApprovalEvent(owner, spender, amount) 213 | 214 | return True 215 | 216 | 217 | def transferFrom(spender,from_acct,to_acct,amount): 218 | """ 219 | spender spends amount of tokens on the behalf of from_acct, spender makes a transaction of amount of tokens 220 | from from_acct to to_acct 221 | :param spender: 222 | :param from_acct: 223 | :param to_acct: 224 | :param amount: 225 | :return: 226 | """ 227 | if len(spender) != 20 or len(from_acct) != 20 or len(to_acct) != 20: 228 | raise Exception("address length error") 229 | if CheckWitness(spender) == False: 230 | return False 231 | 232 | fromKey = concat(BALANCE_PREFIX, from_acct) 233 | fromBalance = Get(ctx, fromKey) 234 | if amount > fromBalance or amount < 0: 235 | return False 236 | 237 | approveKey = concat(concat(APPROVE_PREFIX,from_acct),spender) 238 | approvedAmount = Get(ctx,approveKey) 239 | toKey = concat(BALANCE_PREFIX,to_acct) 240 | 241 | if amount > approvedAmount: 242 | return False 243 | elif amount == approvedAmount: 244 | Delete(ctx,approveKey) 245 | Put(ctx, fromKey, fromBalance - amount) 246 | else: 247 | Put(ctx,approveKey,approvedAmount - amount) 248 | Put(ctx, fromKey, fromBalance - amount) 249 | 250 | toBalance = Get(ctx, toKey) 251 | Put(ctx, toKey, toBalance + amount) 252 | 253 | # Notify(["transfer", AddressToBase58(from_acct), AddressToBase58(to_acct), amount]) 254 | # TransferEvent(AddressToBase58(from_acct), AddressToBase58(to_acct), amount) 255 | TransferEvent(from_acct, to_acct, amount) 256 | 257 | return True 258 | 259 | 260 | def allowance(owner,spender): 261 | """ 262 | check how many token the spender is allowed to spend from owner account 263 | :param owner: token owner 264 | :param spender: token spender 265 | :return: the allowed amount of tokens 266 | """ 267 | key = concat(concat(APPROVE_PREFIX,owner),spender) 268 | return Get(ctx,key) -------------------------------------------------------------------------------- /OEP4Sample/OEP4Sample_compiler2.0.py: -------------------------------------------------------------------------------- 1 | OntCversion = '2.0.0' 2 | """ 3 | An Example of OEP-4 4 | """ 5 | from ontology.interop.System.Storage import GetContext, Get, Put, Delete 6 | from ontology.interop.System.Runtime import Notify, CheckWitness 7 | from ontology.interop.System.Action import RegisterAction 8 | from ontology.builtins import concat 9 | from ontology.interop.Ontology.Runtime import Base58ToAddress 10 | 11 | TransferEvent = RegisterAction("transfer", "from", "to", "amount") 12 | ApprovalEvent = RegisterAction("approval", "owner", "spender", "amount") 13 | 14 | ctx = GetContext() 15 | 16 | NAME = 'MyToken' 17 | SYMBOL = 'MYT' 18 | DECIMALS = 8 19 | FACTOR = 100000000 20 | OWNER = Base58ToAddress("AQf4Mzu1YJrhz9f3aRkkwSm9n3qhXGSh4p") 21 | # OWNER = bytearray(b'\x61\x6f\x2a\x4a\x38\x39\x6f\xf2\x03\xea\x01\xe6\xc0\x70\xae\x42\x1b\xb8\xce\x2d') 22 | TOTAL_AMOUNT = 1000000000 23 | BALANCE_PREFIX = bytearray(b'\x01') 24 | APPROVE_PREFIX = b'\x02' 25 | SUPPLY_KEY = 'TotalSupply' 26 | 27 | 28 | def Main(operation, args): 29 | """ 30 | :param operation: 31 | :param args: 32 | :return: 33 | """ 34 | # 'init' has to be invokded first after deploying the contract to store the necessary and important info in the blockchain 35 | if operation == 'init': 36 | return init() 37 | if operation == 'name': 38 | return name() 39 | if operation == 'symbol': 40 | return symbol() 41 | if operation == 'decimals': 42 | return decimals() 43 | if operation == 'totalSupply': 44 | return totalSupply() 45 | if operation == 'balanceOf': 46 | if len(args) != 1: 47 | return False 48 | acct = args[0] 49 | return balanceOf(acct) 50 | if operation == 'transfer': 51 | if len(args) != 3: 52 | return False 53 | else: 54 | from_acct = args[0] 55 | to_acct = args[1] 56 | amount = args[2] 57 | return transfer(from_acct,to_acct,amount) 58 | if operation == 'transferMulti': 59 | return transferMulti(args) 60 | if operation == 'transferFrom': 61 | if len(args) != 4: 62 | return False 63 | spender = args[0] 64 | from_acct = args[1] 65 | to_acct = args[2] 66 | amount = args[3] 67 | return transferFrom(spender,from_acct,to_acct,amount) 68 | if operation == 'approve': 69 | if len(args) != 3: 70 | return False 71 | owner = args[0] 72 | spender = args[1] 73 | amount = args[2] 74 | return approve(owner,spender,amount) 75 | if operation == 'allowance': 76 | if len(args) != 2: 77 | return False 78 | owner = args[0] 79 | spender = args[1] 80 | return allowance(owner,spender) 81 | return False 82 | 83 | def init(): 84 | """ 85 | initialize the contract, put some important info into the storage in the blockchain 86 | :return: 87 | """ 88 | 89 | if len(OWNER) != 20: 90 | Notify(["Owner illegal!"]) 91 | return False 92 | if Get(ctx,SUPPLY_KEY): 93 | Notify("Already initialized!") 94 | return False 95 | else: 96 | total = TOTAL_AMOUNT * FACTOR 97 | Put(ctx,SUPPLY_KEY,total) 98 | Put(ctx,concat(BALANCE_PREFIX,OWNER),total) 99 | 100 | # Notify(["transfer", "", Base58ToAddress(OWNER), total]) 101 | # ownerBase58 = AddressToBase58(OWNER) 102 | TransferEvent("", OWNER, total) 103 | 104 | return True 105 | 106 | 107 | def name(): 108 | """ 109 | :return: name of the token 110 | """ 111 | return NAME 112 | 113 | 114 | def symbol(): 115 | """ 116 | :return: symbol of the token 117 | """ 118 | return SYMBOL 119 | 120 | 121 | def decimals(): 122 | """ 123 | :return: the decimals of the token 124 | """ 125 | return DECIMALS 126 | 127 | 128 | def totalSupply(): 129 | """ 130 | :return: the total supply of the token 131 | """ 132 | return Get(ctx, SUPPLY_KEY) 133 | 134 | 135 | def balanceOf(account): 136 | """ 137 | :param account: 138 | :return: the token balance of account 139 | """ 140 | if len(account) != 20: 141 | raise Exception("address length error") 142 | return Get(ctx,concat(BALANCE_PREFIX,account)) 143 | 144 | 145 | def transfer(from_acct,to_acct,amount): 146 | """ 147 | Transfer amount of tokens from from_acct to to_acct 148 | :param from_acct: the account from which the amount of tokens will be transferred 149 | :param to_acct: the account to which the amount of tokens will be transferred 150 | :param amount: the amount of the tokens to be transferred, >= 0 151 | :return: True means success, False or raising exception means failure. 152 | """ 153 | if len(to_acct) != 20 or len(from_acct) != 20: 154 | raise Exception("address length error") 155 | if CheckWitness(from_acct) == False or amount < 0: 156 | return False 157 | 158 | fromKey = concat(BALANCE_PREFIX,from_acct) 159 | fromBalance = Get(ctx,fromKey) 160 | if amount > fromBalance: 161 | return False 162 | if amount == fromBalance: 163 | Delete(ctx,fromKey) 164 | else: 165 | Put(ctx,fromKey,fromBalance - amount) 166 | 167 | toKey = concat(BALANCE_PREFIX,to_acct) 168 | toBalance = Get(ctx,toKey) 169 | Put(ctx,toKey,toBalance + amount) 170 | 171 | # Notify(["transfer", AddressToBase58(from_acct), AddressToBase58(to_acct), amount]) 172 | # TransferEvent(AddressToBase58(from_acct), AddressToBase58(to_acct), amount) 173 | TransferEvent(from_acct, to_acct, amount) 174 | 175 | return True 176 | 177 | 178 | def transferMulti(args): 179 | """ 180 | :param args: the parameter is an array, containing element like [from, to, amount] 181 | :return: True means success, False or raising exception means failure. 182 | """ 183 | for p in args: 184 | if len(p) != 3: 185 | # return False is wrong 186 | raise Exception("transferMulti params error.") 187 | if transfer(p[0], p[1], p[2]) == False: 188 | # return False is wrong since the previous transaction will be successful 189 | raise Exception("transferMulti failed.") 190 | return True 191 | 192 | 193 | def approve(owner,spender,amount): 194 | """ 195 | owner allow spender to spend amount of token from owner account 196 | Note here, the amount should be less than the balance of owner right now. 197 | :param owner: 198 | :param spender: 199 | :param amount: amount>=0 200 | :return: True means success, False or raising exception means failure. 201 | """ 202 | if len(spender) != 20 or len(owner) != 20: 203 | raise Exception("address length error") 204 | if CheckWitness(owner) == False: 205 | return False 206 | if amount > balanceOf(owner) or amount < 0: 207 | return False 208 | 209 | key = concat(concat(APPROVE_PREFIX,owner),spender) 210 | Put(ctx, key, amount) 211 | 212 | # Notify(["approval", AddressToBase58(owner), AddressToBase58(spender), amount]) 213 | # ApprovalEvent(AddressToBase58(owner), AddressToBase58(spender), amount) 214 | ApprovalEvent(owner, spender, amount) 215 | 216 | return True 217 | 218 | 219 | def transferFrom(spender,from_acct,to_acct,amount): 220 | """ 221 | spender spends amount of tokens on the behalf of from_acct, spender makes a transaction of amount of tokens 222 | from from_acct to to_acct 223 | :param spender: 224 | :param from_acct: 225 | :param to_acct: 226 | :param amount: 227 | :return: 228 | """ 229 | if len(spender) != 20 or len(from_acct) != 20 or len(to_acct) != 20: 230 | raise Exception("address length error") 231 | if CheckWitness(spender) == False: 232 | return False 233 | 234 | fromKey = concat(BALANCE_PREFIX, from_acct) 235 | fromBalance = Get(ctx, fromKey) 236 | if amount > fromBalance or amount < 0: 237 | return False 238 | 239 | approveKey = concat(concat(APPROVE_PREFIX,from_acct),spender) 240 | approvedAmount = Get(ctx,approveKey) 241 | toKey = concat(BALANCE_PREFIX,to_acct) 242 | 243 | if amount > approvedAmount: 244 | return False 245 | elif amount == approvedAmount: 246 | Delete(ctx,approveKey) 247 | Put(ctx, fromKey, fromBalance - amount) 248 | else: 249 | Put(ctx,approveKey,approvedAmount - amount) 250 | Put(ctx, fromKey, fromBalance - amount) 251 | 252 | toBalance = Get(ctx, toKey) 253 | Put(ctx, toKey, toBalance + amount) 254 | 255 | # Notify(["transfer", AddressToBase58(from_acct), AddressToBase58(to_acct), amount]) 256 | # TransferEvent(AddressToBase58(from_acct), AddressToBase58(to_acct), amount) 257 | TransferEvent(from_acct, to_acct, amount) 258 | 259 | return True 260 | 261 | 262 | def allowance(owner,spender): 263 | """ 264 | check how many token the spender is allowed to spend from owner account 265 | :param owner: token owner 266 | :param spender: token spender 267 | :return: the allowed amount of tokens 268 | """ 269 | key = concat(concat(APPROVE_PREFIX,owner),spender) 270 | return Get(ctx,key) -------------------------------------------------------------------------------- /OEP35Sample/TokenProxy.py: -------------------------------------------------------------------------------- 1 | from ontology.libont import AddressFromVmCode, bytes2hexstring, bytearray_reverse 2 | from ontology.interop.Ontology.Native import Invoke 3 | from ontology.interop.Ontology.Runtime import Base58ToAddress 4 | from ontology.builtins import bytearray, hash160, Exception, state 5 | from ontology.interop.System.Storage import Put, GetContext, Get, Delete 6 | from ontology.interop.System.App import RegisterAppCall, DynamicAppCall 7 | from ontology.interop.System.ExecutionEngine import GetExecutingScriptHash 8 | from ontology.interop.System.Runtime import CheckWitness, Notify, Serialize, Deserialize, GetTime 9 | 10 | #################################### 11 | ctx = GetContext() 12 | ContractAddress = GetExecutingScriptHash() 13 | Admin = Base58ToAddress('AGjD4Mo25kzcStyh1stp7tXkUuMopD43NT') 14 | ONTAddress = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') 15 | ONGAddress = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') 16 | 17 | #################Prefix Setting#################### 18 | TOKEN_SYMBOL_PREFIX = 'TOKEN_SYMBOL' 19 | TOKEN_HASH_PREFIX = 'TOKEN_HASH' 20 | SUPPORTED_TOKEN = 'SUPPORTED_TOKEN' 21 | REGISTERED_EXCHANGE = 'REGISTERED_EXCHANGE' 22 | EXCHANGE_ID_PREFIX = "EXCHANGE_ID" 23 | EXCHANGE_NAME_PREFIX = "EXCHANGE_NAME" 24 | 25 | def Main(operation, args): 26 | 27 | if operation == "AddToken": 28 | symbol = args[0] 29 | hash = args[1] 30 | return AddToken(symbol, hash) 31 | 32 | if operation == "RemoveToken": 33 | symbol = args[0] 34 | hash = args[1] 35 | return RemoveToken(symbol, hash) 36 | 37 | if operation == "GetTokens": 38 | return GetTokens() 39 | 40 | if operation == "RegisterExchange": 41 | exchangeName = args[0] 42 | exchangeId = args[1] 43 | return RegisterExchange(exchangeName, exchangeId) 44 | 45 | if operation == "RemoveExchange": 46 | exchangeName = args[0] 47 | exchangeId = args[1] 48 | return RemoveExchange(exchangeName, exchangeId) 49 | 50 | if operation == "GetAuthorizedExchange": 51 | return GetAuthorizedExchange() 52 | 53 | # if operation == "Approve": 54 | # return Approve() 55 | # 56 | # if operation == "Allowance": 57 | # return Allowance() 58 | 59 | if operation == "TokenTransferFrom": 60 | exchangeId = args[0] 61 | order = args[1] 62 | return TokenTransferFrom(exchangeId, order) 63 | 64 | if operation == "TokenTransferFromMulti": 65 | exchangeId = args[0] 66 | orders = args[1] 67 | return TokenTransferFromMulti(exchangeId, orders) 68 | 69 | def AddToken(symbol, hash): 70 | """ 71 | :param symbol:token symbol, like "ONT", "ONG" 72 | :param hash: token script hash,such as ONT,ONG or other token hash, if success, this token can be exchanged on the exchange. 73 | :return:True or False 74 | """ 75 | require(CheckWitness(Admin), "not admin") 76 | require(validateAddress(hash), "invalid contract hash") 77 | 78 | if Get(ctx, concatKey(TOKEN_SYMBOL_PREFIX, symbol)): 79 | return False 80 | if Get(ctx, concatKey(TOKEN_HASH_PREFIX, hash)): 81 | return False 82 | 83 | 84 | 85 | supportToken = Get(ctx, SUPPORTED_TOKEN) 86 | 87 | if not supportToken: 88 | tokenMap = { 89 | symbol: hash 90 | } 91 | else: 92 | tokenMap = Deserialize(supportToken) 93 | tokenMap[symbol] = hash 94 | 95 | Put(ctx, concatKey(TOKEN_SYMBOL_PREFIX, symbol), symbol) 96 | Put(ctx, concatKey(TOKEN_HASH_PREFIX, hash), hash) 97 | Put(ctx, SUPPORTED_TOKEN, Serialize(tokenMap)) 98 | 99 | return True 100 | 101 | def RemoveToken(symbol, hash): 102 | """ 103 | 104 | :param symbol:token symbol, like "ONT", "ONG" 105 | :param hash: this token will be removed from the exchange. 106 | :return:True or False 107 | """ 108 | require(CheckWitness(Admin), "not admin") 109 | 110 | supportToken = Get(ctx, SUPPORTED_TOKEN) 111 | if not supportToken: 112 | return False 113 | 114 | tokenMap = Deserialize(supportToken) 115 | 116 | if tokenMap[symbol] != hash: 117 | return False 118 | 119 | tokenMap.remove(symbol) 120 | Put(ctx, SUPPORTED_TOKEN, Serialize(tokenMap)) 121 | Delete(ctx, concatKey(TOKEN_SYMBOL_PREFIX, symbol)) 122 | Delete(ctx, concatKey(TOKEN_HASH_PREFIX, hash)) 123 | 124 | return True 125 | 126 | def GetTokens(): 127 | """ 128 | 129 | :return:Get all the tokens that can be traded in the exchange, it will response a map data structure. 130 | """ 131 | return Get(ctx, SUPPORTED_TOKEN) 132 | 133 | def RegisterExchange(exchangeName, exchangeId): 134 | """ 135 | 136 | :param exchangeName:exchange name, like "Huobi" 137 | :param exchangeId:exchange Id, essentially it is also a wallet address, only this exchange account can invoke token proxy smart contract 138 | :return:True or False 139 | """ 140 | require(CheckWitness(Admin), "not admin") 141 | require(validateAddress(exchangeId), "invalid exchange Id") 142 | require(exchangeName != "", "invalid exchange name") 143 | 144 | if Get(ctx, concatKey(EXCHANGE_NAME_PREFIX, exchangeName)): 145 | return False 146 | if Get(ctx, concatKey(EXCHANGE_ID_PREFIX, exchangeId)): 147 | return False 148 | 149 | registerExchange = Get(ctx, REGISTERED_EXCHANGE) 150 | 151 | if not registerExchange: 152 | exchangeMap = { 153 | exchangeName: exchangeId 154 | } 155 | else: 156 | exchangeMap = Deserialize(registerExchange) 157 | exchangeMap[exchangeName] = exchangeId 158 | 159 | Put(ctx, concatKey(EXCHANGE_NAME_PREFIX, exchangeName), exchangeName) 160 | Put(ctx, concatKey(EXCHANGE_ID_PREFIX, exchangeId), exchangeId) 161 | Put(ctx, REGISTERED_EXCHANGE, Serialize(exchangeMap)) 162 | 163 | def RemoveExchange(exchangeName, exchangeId): 164 | """ 165 | 166 | :param exchangeName:exchange name, like "Huobi" 167 | :param exchangeId:exchange Id 168 | :return:True or False, if success, the exchange can't invoke token proxy smart contract. 169 | """ 170 | require(CheckWitness(Admin), "not admin") 171 | 172 | registerExchange = Get(ctx, REGISTERED_EXCHANGE) 173 | if not registerExchange: 174 | return False 175 | 176 | exchangeMap = Deserialize(registerExchange) 177 | 178 | if exchangeMap[exchangeName] != exchangeId: 179 | return False 180 | 181 | exchangeMap.remove(exchangeName) 182 | Put(ctx, REGISTERED_EXCHANGE, Serialize(exchangeMap)) 183 | Delete(ctx, concatKey(EXCHANGE_NAME_PREFIX, exchangeName)) 184 | Delete(ctx, concatKey(EXCHANGE_ID_PREFIX, exchangeId)) 185 | 186 | return True 187 | 188 | 189 | def GetAuthorizedExchange(): 190 | """ 191 | 192 | :return:Get all the exchange list that can invoke token proxy smart contract. 193 | """ 194 | return Get(ctx, REGISTERED_EXCHANGE) 195 | 196 | def TokenTransferFrom(exchangeId, order): 197 | """ 198 | 199 | :param exchangeId:exchange id, Invoked only by registered exchange 200 | :param order: the order is a map structure. 201 | :return:True or false, if success, the maker and taker will get purpose token. 202 | """ 203 | expireTime = order['expireTime'] 204 | require(GetTime() <= expireTime, "order expired") 205 | require(CheckWitness(exchangeId), "invalid exchange") 206 | 207 | if not Get(ctx, concatKey(EXCHANGE_ID_PREFIX, exchangeId)): 208 | return False 209 | 210 | maker = order['makerAddress'] 211 | makerAmount = order['makerAssetAmount'] 212 | makerHash = order['makerTokenHash'] 213 | taker = order['takerAddress'] 214 | takerAmount = order['takerAssetAmount'] 215 | takerHash = order['takerTokenHash'] 216 | makerFee = order['makerFee'] 217 | takerFee = order['takerFee'] 218 | feeTokenHash = order['feeTokenHash'] 219 | feeFeceiver = order['feeReceiver'] 220 | 221 | if isNativeAsset(makerHash): 222 | require(transferFromNative(makerHash, ContractAddress, maker, taker, makerAmount), "transfer from maker asset failed") 223 | else: 224 | require(DynamicAppCall(bytearray_reverse(makerHash), "transferFrom", [ContractAddress, maker, taker, makerAmount]), "transfer maker token to taker failed") 225 | 226 | Notify("111") 227 | 228 | if isNativeAsset(takerHash): 229 | require(transferFromNative(takerHash, ContractAddress, taker, maker, takerAmount), "transfer from taker asset failed") 230 | else: 231 | require(DynamicAppCall(bytearray_reverse(takerHash), "transferFrom", [ContractAddress, taker, maker, takerAmount]), "transfer taker token to maker failed") 232 | 233 | Notify("222") 234 | 235 | if isNativeAsset(feeTokenHash): 236 | require(transferFromNative(feeTokenHash, ContractAddress, maker, feeFeceiver, makerFee), "charge maker fee failed") 237 | require(transferFromNative(feeTokenHash, ContractAddress, taker, feeFeceiver, takerFee), "charge taker fee failed") 238 | else: 239 | require(DynamicAppCall(bytearray_reverse(feeTokenHash), "transferFrom", [ContractAddress, maker, feeFeceiver, makerFee]), "charge maker fee failed") 240 | require(DynamicAppCall(bytearray_reverse(feeTokenHash), "transferFrom", [ContractAddress, taker, feeFeceiver, takerFee]), "charge taker fee failed") 241 | 242 | Notify("success") 243 | 244 | return True 245 | 246 | def TokenTransferFromMulti(exchangeId, orders): 247 | """ 248 | 249 | :param exchangeId:exchange id, Invoked only by registered exchange 250 | :param orders: orders is "TokenTransferFromMulti" fucntion parameter order array list. 251 | :return: 252 | """ 253 | require(CheckWitness(exchangeId), "invalid exchange") 254 | if not Get(ctx, concatKey(EXCHANGE_ID_PREFIX, exchangeId)): 255 | return False 256 | 257 | for order in orders: 258 | if len(order) != 1: 259 | raise Exception("TokenTransferFromMulti params error.") 260 | if TokenTransferFrom(exchangeId, order) == False: 261 | raise Exception("TokenTransferFrom failed.") 262 | return True 263 | 264 | def concatKey(str1, str2): 265 | """ 266 | connect str1 and str2 together as a key 267 | :param str1: string1 268 | :param str2: string2 269 | :return: string1_string2 270 | """ 271 | return concat(concat(str1, '_'), str2) 272 | 273 | def require(condition, msg): 274 | if not condition: 275 | raise Exception(msg) 276 | return True 277 | 278 | def transferONG(fromAcct, toAcct, amount): 279 | """ 280 | transfer ONG 281 | :param fromacct: 282 | :param toacct: 283 | :param amount: 284 | :return: 285 | """ 286 | param = state(fromAcct, toAcct, amount) 287 | res = Invoke(0, ONGAddress, 'transfer', [param]) 288 | if res and res == b'\x01': 289 | return True 290 | else: 291 | return False 292 | 293 | def transferFromNative(contractAddress, spender, fromAcct, toAcct, amount): 294 | """ 295 | transferFrom ONT/ONG 296 | :param spender: 297 | :param fromacct: 298 | :param toacct: 299 | :param amount: 300 | :return: 301 | """ 302 | param = state(spender, fromAcct, toAcct, amount) 303 | res = Invoke(0, contractAddress, 'transferFrom', param) 304 | if res and res == b'\x01': 305 | return True 306 | else: 307 | return False 308 | 309 | def validateAddress(address): 310 | if len(address) != 20: 311 | return False 312 | 313 | return True 314 | 315 | def isNativeAsset(address): 316 | if address == ONTAddress or address == ONGAddress: 317 | return True 318 | 319 | return False 320 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /OEP5Sample/OEP5Sample.py: -------------------------------------------------------------------------------- 1 | """ 2 | A sample of OEP5 smart contract 3 | """ 4 | from boa.interop.System.Storage import GetContext, Get, Put, Delete 5 | from boa.interop.System.Runtime import CheckWitness, GetTime, Notify, Serialize, Deserialize 6 | from boa.interop.System.ExecutionEngine import GetExecutingScriptHash 7 | from boa.builtins import ToScriptHash, sha256, concat 8 | 9 | 10 | from boa.interop.System.Runtime import Serialize, Deserialize 11 | 12 | 13 | 14 | 15 | # modify to the admin address 16 | admin = ToScriptHash('AQf4Mzu1YJrhz9f3aRkkwSm9n3qhXGSh4p') 17 | 18 | NAME = 'My Non-Fungibles Token' 19 | SYMBOL = 'MNFT' 20 | 21 | 22 | OWNER_BALANCE_PREFIX = 'Balance' 23 | OWNER_OF_TOKEN_PREFIX = 'OwnerOf' 24 | APPROVE_PREFIX = 'Approve' 25 | TOKEN_ID_PREFIX = 'TokenID' 26 | # TOKEN_INDEX_PREFIX, INITED, and TOTAL_SUPPLY for testing usage only 27 | TOKEN_INDEX_PREFIX = 'Index' 28 | TOTAL_SUPPLY = 'TotalSupply' 29 | INITED = 'Initialized' 30 | 31 | 32 | ctx = GetContext() 33 | selfAddr = GetExecutingScriptHash() 34 | 35 | def Main(operation, args): 36 | if operation == 'name': 37 | return name() 38 | if operation == 'symbol': 39 | return symbol() 40 | if operation == 'balanceOf': 41 | if len(args) != 1: 42 | return False 43 | owner = args[0] 44 | return balanceOf(owner) 45 | if operation == 'ownerOf': 46 | if len(args) != 1: 47 | return False 48 | tokenID = args[0] 49 | return ownerOf(tokenID) 50 | if operation == 'transfer': 51 | if len(args) != 2: 52 | return False 53 | toAcct = args[0] 54 | tokenID = args[1] 55 | return transfer(toAcct, tokenID) 56 | if operation == 'transferMulti': 57 | return transferMulti(args) 58 | if operation == 'approve': 59 | if len(args) != 2: 60 | return False 61 | toAcct = args[0] 62 | tokenID = args[1] 63 | return approve(toAcct, tokenID) 64 | if operation == 'takeOwnership': 65 | if len(args) != 2: 66 | return False 67 | toAcct = args[0] 68 | tokenID = args[1] 69 | return takeOwnership(toAcct, tokenID) 70 | 71 | ############ For testing usage only starts ############ 72 | if operation == 'init': 73 | return init() 74 | if operation == 'queryTokenByID': 75 | if len(args) != 1: 76 | return False 77 | tokenID = args[0] 78 | return queryTokenByID(tokenID) 79 | if operation == 'totalSupply': 80 | return totalSupply() 81 | if operation == "getApproved": 82 | if len(args) != 1: 83 | return False 84 | tokenID = args[0] 85 | return getApproved(tokenID) 86 | if operation == "queryTokenIDByIndex": 87 | if len(args) != 1: 88 | return False 89 | index = args[0] 90 | return queryTokenIDByIndex(index) 91 | ############ For testing usage only ends ############ 92 | return False 93 | 94 | 95 | def name(): 96 | """ 97 | :return: name of the token 98 | """ 99 | return NAME 100 | 101 | 102 | def symbol(): 103 | """ 104 | :return: symbol of the token 105 | """ 106 | return SYMBOL 107 | 108 | 109 | def balanceOf(owner): 110 | """ 111 | :param owner: 112 | :return: token balance of the owner 113 | """ 114 | if len(owner) != 20: 115 | return False 116 | key = concatkey(OWNER_BALANCE_PREFIX, owner) 117 | return Get(ctx, key) 118 | 119 | 120 | def ownerOf(tokenID): 121 | """ 122 | get the owner of the unique token with this tokenID 123 | :param tokenID: the tokenID should be unique and exist. 124 | :return: the owner address of the token with this unique tokenID 125 | """ 126 | key = concatkey(OWNER_OF_TOKEN_PREFIX, tokenID) 127 | owner = Get(ctx, key) 128 | if not owner: 129 | raise Exception('ownerOf failed!') 130 | return owner 131 | 132 | 133 | def transfer(toAcct, tokenID): 134 | """ 135 | transfer the token with tokenID to the toAcct 136 | :param toAcct: to account address 137 | :param tokenID: the unique token's ID, type should be ByteArray 138 | :return: False means failure, True means success. 139 | """ 140 | tokenOwner = ownerOf(tokenID) 141 | if CheckWitness(tokenOwner) == False: 142 | return False 143 | if len(toAcct) != 20: 144 | raise Exception('address length error!') 145 | 146 | ownerKey = concatkey(OWNER_OF_TOKEN_PREFIX, tokenID) 147 | fromAcct = Get(ctx, ownerKey) 148 | balanceKey = concatkey(OWNER_BALANCE_PREFIX, fromAcct) 149 | fromBalance = Get(ctx, balanceKey) 150 | if fromBalance >= 1: 151 | # decrease fromAccount token balance 152 | Put(ctx, balanceKey, fromBalance - 1) 153 | else: 154 | raise Exception('fromBalance error') 155 | # set the owner of tokenID to toAcct 156 | Put(ctx, ownerKey, toAcct) 157 | # increase toAccount token balance 158 | balanceKey = concatkey(OWNER_BALANCE_PREFIX, toAcct) 159 | Put(ctx, balanceKey, balanceOf(toAcct) + 1) 160 | 161 | Delete(ctx, concatkey(APPROVE_PREFIX, tokenID)) 162 | Notify(['transfer', fromAcct, toAcct, tokenID]) 163 | 164 | return True 165 | 166 | 167 | def transferMulti(args): 168 | ''' 169 | multi transfer 170 | :param args:[[toAccount1, tokenID1],[toAccount2, tokenID2]] 171 | :return: True or raise exception 172 | ''' 173 | for p in args: 174 | if len(p) != 2: 175 | raise Exception('transferMulti failed - input error!') 176 | if transfer(p[0], p[1]) == False: 177 | raise Exception('transferMulti failed - transfer error!') 178 | return True 179 | 180 | 181 | def approve(toAcct, tokenID): 182 | ''' 183 | approve the token to toAcct address, it can overwrite older approved address 184 | :param toAcct: 185 | :param tokenID: 186 | :return: 187 | ''' 188 | tokenOwner = ownerOf(tokenID) 189 | if CheckWitness(tokenOwner) == False: 190 | return False 191 | if len(toAcct) != 20: 192 | raise Exception('address length error!') 193 | 194 | Put(GetContext(), concatkey(APPROVE_PREFIX, tokenID), toAcct) 195 | Notify(['approval', tokenOwner, toAcct, tokenID]) 196 | return True 197 | 198 | 199 | def takeOwnership(toAcct, tokenID): 200 | """ 201 | transfer the approved tokenId token to toAcct 202 | the invoker can be the owner or the approved account 203 | toAcct can be any address 204 | :param toAcct: the account that will be assigned as the new owner of tokenId 205 | :param tokenID: the tokenId token will be assigned to toAcct 206 | :return: False or True 207 | """ 208 | if len(toAcct) != 20: 209 | raise Exception("toAcct illegal!") 210 | tokenOwner = ownerOf(tokenID) 211 | 212 | if not tokenOwner: 213 | return False 214 | approveKey = concatkey(APPROVE_PREFIX, tokenID) 215 | approvedAcct = Get(ctx, approveKey) 216 | 217 | if CheckWitness(tokenOwner) != True and CheckWitness(approvedAcct) != True: 218 | return False 219 | 220 | Delete(ctx, approveKey) 221 | ownerKey = concatkey(OWNER_OF_TOKEN_PREFIX, tokenID) 222 | Put(ctx, ownerKey, toAcct) 223 | 224 | fromBalance = balanceOf(tokenOwner) 225 | toBalance = balanceOf(toAcct) 226 | # to avoid overflow 227 | if fromBalance >= 1 and toBalance < toBalance + 1: 228 | Put(ctx, concatkey(OWNER_BALANCE_PREFIX, tokenOwner), fromBalance - 1) 229 | Put(ctx, concatkey(OWNER_BALANCE_PREFIX, toAcct), toBalance + 1) 230 | 231 | Notify(['transfer', tokenOwner, toAcct, tokenID]) 232 | 233 | return True 234 | 235 | 236 | def concatkey(str1, str2): 237 | return concat(concat(str1, '_'), str2) 238 | 239 | #################### For testing usage only starts ###################### 240 | 241 | def init(): 242 | ''' 243 | based on your requirements, initialize the tokens 244 | :return: 245 | ''' 246 | # Notify(["111_init"]) 247 | if not Get(ctx, INITED) and CheckWitness(admin) == True: 248 | Put(ctx, INITED, 'TRUE') 249 | Put(ctx, TOTAL_SUPPLY, 0) 250 | tt = createMultiTokens() 251 | if tt == True: 252 | # adminBalance = Get(ctx, concatkey(OWNER_BALANCE_PREFIX, admin)) 253 | # Put(ctx, TOTAL_SUPPLY, adminBalance) 254 | # Notify(["222_init", adminBalance]) 255 | return True 256 | return False 257 | else: 258 | Notify(["222_init"]) 259 | 260 | return False 261 | 262 | 263 | def totalSupply(): 264 | return Get(ctx, TOTAL_SUPPLY) 265 | 266 | 267 | def queryTokenIDByIndex(idx): 268 | ''' 269 | query tokenid by index 270 | :param idx: 271 | :return: 272 | ''' 273 | tokenID = Get(ctx, concatkey(TOKEN_INDEX_PREFIX, idx)) 274 | # Notify(["111_queryTokenIDByIndex", tokenID]) 275 | return tokenID 276 | 277 | 278 | def queryTokenByID(tokenID): 279 | ''' 280 | query token detail by tokenID 281 | :param tokenID: 282 | :return: 283 | ''' 284 | Notify(["111_queryTokenByID", tokenID, concatkey(TOKEN_ID_PREFIX, tokenID)]) 285 | token = Get(ctx, concatkey(TOKEN_ID_PREFIX, tokenID)) 286 | token_info = Deserialize(token) 287 | id = token_info['ID'] 288 | name = token_info['Name'] 289 | image = token_info['Image'] 290 | type = token_info['Type'] 291 | # Notify(["111_token info: ", id, name, image, type]) 292 | return [id, name, image, type] 293 | 294 | 295 | def getApproved(tokenID): 296 | ''' 297 | get the approved address of the token 298 | :param tokenID: 299 | :return: 300 | ''' 301 | key = concatkey(APPROVE_PREFIX, tokenID) 302 | return Get(ctx, key) 303 | 304 | 305 | def createMultiTokens(): 306 | # Notify(["111_createMultiTokens begins"]) 307 | 308 | a1 = {'Name': 'HEART A', 'Image': 'http://images.com/hearta.jpg'} 309 | a2 = {'Name': 'HEART 2', 'Image': 'http://images.com/heart2.jpg'} 310 | a3 = {'Name': 'HEART 3', 'Image': 'http://images.com/heart3.jpg'} 311 | a4 = {'Name': 'HEART 4', 'Image': 'http://images.com/heart4.jpg'} 312 | a5 = {'Name': 'HEART 5', 'Image': 'http://images.com/heart5.jpg'} 313 | a6 = {'Name': 'HEART 6', 'Image': 'http://images.com/heart6.jpg'} 314 | a7 = {'Name': 'HEART 7', 'Image': 'http://images.com/heart7.jpg'} 315 | a8 = {'Name': 'HEART 8', 'Image': 'http://images.com/heart8.jpg'} 316 | a9 = {'Name': 'HEART 9', 'Image': 'http://images.com/heart9.jpg'} 317 | a10 = {'Name': 'HEART 10', 'Image': 'http://images.com/heart10.jpg'} 318 | a11 = {'Name': 'HEART J', 'Image': 'http://images.com/heartj.jpg'} 319 | a12 = {'Name': 'HEART Q', 'Image': 'http://images.com/heartq.jpg'} 320 | a13 = {'Name': 'HEART K', 'Image': 'http://images.com/heartk.jpg'} 321 | 322 | cards = [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13] 323 | for card in cards: 324 | if createOneToken(card['Name'], card['Image'], 'CARD') != True: 325 | raise Exception('_createMultiToken failed') 326 | # Notify(["222_createMultiTokens ends"]) 327 | return True 328 | 329 | 330 | def createOneToken(name, url, type): 331 | ''' 332 | create a new token 333 | :param name: 334 | :param url: 335 | :param type: 336 | :return: 337 | ''' 338 | # Notify(["111_createOneToken begins"]) 339 | # generate tokenID 340 | timestamp = GetTime() 341 | totalSupply = Get(ctx, TOTAL_SUPPLY) 342 | newTotalSupply = totalSupply + 1 343 | Put(ctx, TOTAL_SUPPLY, newTotalSupply) 344 | tmp = concatkey(concatkey(selfAddr, timestamp), newTotalSupply) 345 | tokenID = sha256(tmp) 346 | # construct token map 347 | token = {'ID': tokenID, 'Name': name, 'Image': url, 'Type': type} 348 | Notify(["111_createOneToken", newTotalSupply, tokenID, concatkey(TOKEN_ID_PREFIX, tokenID)]) 349 | Put(ctx, concatkey(TOKEN_INDEX_PREFIX, newTotalSupply), tokenID) 350 | ownerKey = concatkey(OWNER_OF_TOKEN_PREFIX, tokenID) 351 | Put(ctx, ownerKey, admin) 352 | Put(ctx, concatkey(TOKEN_ID_PREFIX, tokenID), Serialize(token)) 353 | # add to adminBalance 354 | adminBalance = Get(ctx, concatkey(OWNER_BALANCE_PREFIX, admin)) 355 | Put(ctx, concatkey(OWNER_BALANCE_PREFIX, admin), adminBalance + 1) 356 | # Notify(["333_createOneToken ends"]) 357 | return True 358 | #################### For testing usage only ends ###################### 359 | -------------------------------------------------------------------------------- /OEP5Sample/OEP5Sample_compiler2.0.py: -------------------------------------------------------------------------------- 1 | OntCversion = '2.0.0' 2 | """ 3 | A sample of OEP5 smart contract 4 | """ 5 | from ontology.interop.System.Storage import GetContext, Get, Put, Delete 6 | from ontology.interop.System.Runtime import CheckWitness, GetTime, Notify, Serialize, Deserialize 7 | from ontology.interop.System.ExecutionEngine import GetExecutingScriptHash 8 | from ontology.builtins import sha256, concat 9 | from ontology.interop.Ontology.Runtime import Base58ToAddress 10 | 11 | from ontology.interop.System.Runtime import Serialize, Deserialize 12 | 13 | 14 | 15 | 16 | # modify to the admin address 17 | admin = Base58ToAddress('AQf4Mzu1YJrhz9f3aRkkwSm9n3qhXGSh4p') 18 | 19 | NAME = 'My Non-Fungibles Token' 20 | SYMBOL = 'MNFT' 21 | 22 | 23 | OWNER_BALANCE_PREFIX = 'Balance' 24 | OWNER_OF_TOKEN_PREFIX = 'OwnerOf' 25 | APPROVE_PREFIX = 'Approve' 26 | TOKEN_ID_PREFIX = 'TokenID' 27 | # TOKEN_INDEX_PREFIX, INITED, and TOTAL_SUPPLY for testing usage only 28 | TOKEN_INDEX_PREFIX = 'Index' 29 | TOTAL_SUPPLY = 'TotalSupply' 30 | INITED = 'Initialized' 31 | 32 | 33 | ctx = GetContext() 34 | selfAddr = GetExecutingScriptHash() 35 | 36 | def Main(operation, args): 37 | if operation == 'name': 38 | return name() 39 | if operation == 'symbol': 40 | return symbol() 41 | if operation == 'balanceOf': 42 | if len(args) != 1: 43 | return False 44 | owner = args[0] 45 | return balanceOf(owner) 46 | if operation == 'ownerOf': 47 | if len(args) != 1: 48 | return False 49 | tokenID = args[0] 50 | return ownerOf(tokenID) 51 | if operation == 'transfer': 52 | if len(args) != 2: 53 | return False 54 | toAcct = args[0] 55 | tokenID = args[1] 56 | return transfer(toAcct, tokenID) 57 | if operation == 'transferMulti': 58 | return transferMulti(args) 59 | if operation == 'approve': 60 | if len(args) != 2: 61 | return False 62 | toAcct = args[0] 63 | tokenID = args[1] 64 | return approve(toAcct, tokenID) 65 | if operation == 'takeOwnership': 66 | if len(args) != 2: 67 | return False 68 | toAcct = args[0] 69 | tokenID = args[1] 70 | return takeOwnership(toAcct, tokenID) 71 | 72 | ############ For testing usage only starts ############ 73 | if operation == 'init': 74 | return init() 75 | if operation == 'queryTokenByID': 76 | if len(args) != 1: 77 | return False 78 | tokenID = args[0] 79 | return queryTokenByID(tokenID) 80 | if operation == 'totalSupply': 81 | return totalSupply() 82 | if operation == "getApproved": 83 | if len(args) != 1: 84 | return False 85 | tokenID = args[0] 86 | return getApproved(tokenID) 87 | if operation == "queryTokenIDByIndex": 88 | if len(args) != 1: 89 | return False 90 | index = args[0] 91 | return queryTokenIDByIndex(index) 92 | ############ For testing usage only ends ############ 93 | return False 94 | 95 | 96 | def name(): 97 | """ 98 | :return: name of the token 99 | """ 100 | return NAME 101 | 102 | 103 | def symbol(): 104 | """ 105 | :return: symbol of the token 106 | """ 107 | return SYMBOL 108 | 109 | 110 | def balanceOf(owner): 111 | """ 112 | :param owner: 113 | :return: token balance of the owner 114 | """ 115 | if len(owner) != 20: 116 | return False 117 | key = concatkey(OWNER_BALANCE_PREFIX, owner) 118 | return Get(ctx, key) 119 | 120 | 121 | def ownerOf(tokenID): 122 | """ 123 | get the owner of the unique token with this tokenID 124 | :param tokenID: the tokenID should be unique and exist. 125 | :return: the owner address of the token with this unique tokenID 126 | """ 127 | key = concatkey(OWNER_OF_TOKEN_PREFIX, tokenID) 128 | owner = Get(ctx, key) 129 | if not owner: 130 | raise Exception('ownerOf failed!') 131 | return owner 132 | 133 | 134 | def transfer(toAcct, tokenID): 135 | """ 136 | transfer the token with tokenID to the toAcct 137 | :param toAcct: to account address 138 | :param tokenID: the unique token's ID, type should be ByteArray 139 | :return: False means failure, True means success. 140 | """ 141 | tokenOwner = ownerOf(tokenID) 142 | if CheckWitness(tokenOwner) == False: 143 | return False 144 | if len(toAcct) != 20: 145 | raise Exception('address length error!') 146 | 147 | ownerKey = concatkey(OWNER_OF_TOKEN_PREFIX, tokenID) 148 | fromAcct = Get(ctx, ownerKey) 149 | balanceKey = concatkey(OWNER_BALANCE_PREFIX, fromAcct) 150 | fromBalance = Get(ctx, balanceKey) 151 | if fromBalance >= 1: 152 | # decrease fromAccount token balance 153 | Put(ctx, balanceKey, fromBalance - 1) 154 | else: 155 | raise Exception('fromBalance error') 156 | # set the owner of tokenID to toAcct 157 | Put(ctx, ownerKey, toAcct) 158 | # increase toAccount token balance 159 | balanceKey = concatkey(OWNER_BALANCE_PREFIX, toAcct) 160 | Put(ctx, balanceKey, balanceOf(toAcct) + 1) 161 | Delete(ctx, concatkey(APPROVE_PREFIX, tokenID)) 162 | Notify(['transfer', fromAcct, toAcct, tokenID]) 163 | 164 | return True 165 | 166 | 167 | def transferMulti(args): 168 | ''' 169 | multi transfer 170 | :param args:[[toAccount1, tokenID1],[toAccount2, tokenID2]] 171 | :return: True or raise exception 172 | ''' 173 | for p in args: 174 | if len(p) != 2: 175 | raise Exception('transferMulti failed - input error!') 176 | if transfer(p[0], p[1]) == False: 177 | raise Exception('transferMulti failed - transfer error!') 178 | return True 179 | 180 | 181 | def approve(toAcct, tokenID): 182 | ''' 183 | approve the token to toAcct address, it can overwrite older approved address 184 | :param toAcct: 185 | :param tokenID: 186 | :return: 187 | ''' 188 | tokenOwner = ownerOf(tokenID) 189 | if CheckWitness(tokenOwner) == False: 190 | return False 191 | if len(toAcct) != 20: 192 | raise Exception('address length error!') 193 | 194 | Put(GetContext(), concatkey(APPROVE_PREFIX, tokenID), toAcct) 195 | Notify(['approval', tokenOwner, toAcct, tokenID]) 196 | return True 197 | 198 | 199 | def takeOwnership(toAcct, tokenID): 200 | """ 201 | transfer the approved tokenId token to toAcct 202 | the invoker can be the owner or the approved account 203 | toAcct can be any address 204 | :param toAcct: the account that will be assigned as the new owner of tokenId 205 | :param tokenID: the tokenId token will be assigned to toAcct 206 | :return: False or True 207 | """ 208 | if len(toAcct) != 20: 209 | raise Exception("toAcct illegal!") 210 | tokenOwner = ownerOf(tokenID) 211 | 212 | if not tokenOwner: 213 | return False 214 | approveKey = concatkey(APPROVE_PREFIX, tokenID) 215 | approvedAcct = Get(ctx, approveKey) 216 | 217 | if CheckWitness(tokenOwner) != True and CheckWitness(approvedAcct) != True: 218 | return False 219 | 220 | Delete(ctx, approveKey) 221 | ownerKey = concatkey(OWNER_OF_TOKEN_PREFIX, tokenID) 222 | Put(ctx, ownerKey, toAcct) 223 | 224 | fromBalance = balanceOf(tokenOwner) 225 | toBalance = balanceOf(toAcct) 226 | # to avoid overflow 227 | if fromBalance >= 1 and toBalance < toBalance + 1: 228 | Put(ctx, concatkey(OWNER_BALANCE_PREFIX, tokenOwner), fromBalance - 1) 229 | Put(ctx, concatkey(OWNER_BALANCE_PREFIX, toAcct), toBalance + 1) 230 | 231 | Notify(['transfer', tokenOwner, toAcct, tokenID]) 232 | 233 | return True 234 | 235 | 236 | def concatkey(str1, str2): 237 | return concat(concat(str1, '_'), str2) 238 | 239 | #################### For testing usage only starts ###################### 240 | 241 | def init(): 242 | ''' 243 | based on your requirements, initialize the tokens 244 | :return: 245 | ''' 246 | # Notify(["111_init"]) 247 | if not Get(ctx, INITED) and CheckWitness(admin) == True: 248 | Put(ctx, INITED, 'TRUE') 249 | Put(ctx, TOTAL_SUPPLY, 0) 250 | tt = createMultiTokens() 251 | if tt == True: 252 | # adminBalance = Get(ctx, concatkey(OWNER_BALANCE_PREFIX, admin)) 253 | # Put(ctx, TOTAL_SUPPLY, adminBalance) 254 | # Notify(["222_init", adminBalance]) 255 | return True 256 | return False 257 | else: 258 | Notify(["222_init"]) 259 | 260 | return False 261 | 262 | 263 | def totalSupply(): 264 | return Get(ctx, TOTAL_SUPPLY) 265 | 266 | 267 | def queryTokenIDByIndex(idx): 268 | ''' 269 | query tokenid by index 270 | :param idx: 271 | :return: 272 | ''' 273 | tokenID = Get(ctx, concatkey(TOKEN_INDEX_PREFIX, idx)) 274 | # Notify(["111_queryTokenIDByIndex", tokenID]) 275 | return tokenID 276 | 277 | 278 | def queryTokenByID(tokenID): 279 | ''' 280 | query token detail by tokenID 281 | :param tokenID: 282 | :return: 283 | ''' 284 | Notify(["111_queryTokenByID", tokenID, concatkey(TOKEN_ID_PREFIX, tokenID)]) 285 | token = Get(ctx, concatkey(TOKEN_ID_PREFIX, tokenID)) 286 | token_info = Deserialize(token) 287 | id = token_info['ID'] 288 | name = token_info['Name'] 289 | image = token_info['Image'] 290 | type = token_info['Type'] 291 | # Notify(["111_token info: ", id, name, image, type]) 292 | return [id, name, image, type] 293 | 294 | 295 | def getApproved(tokenID): 296 | ''' 297 | get the approved address of the token 298 | :param tokenID: 299 | :return: 300 | ''' 301 | key = concatkey(APPROVE_PREFIX, tokenID) 302 | return Get(ctx, key) 303 | 304 | 305 | def createMultiTokens(): 306 | # Notify(["111_createMultiTokens begins"]) 307 | 308 | a1 = {'Name': 'HEART A', 'Image': 'http://images.com/hearta.jpg'} 309 | a2 = {'Name': 'HEART 2', 'Image': 'http://images.com/heart2.jpg'} 310 | a3 = {'Name': 'HEART 3', 'Image': 'http://images.com/heart3.jpg'} 311 | a4 = {'Name': 'HEART 4', 'Image': 'http://images.com/heart4.jpg'} 312 | a5 = {'Name': 'HEART 5', 'Image': 'http://images.com/heart5.jpg'} 313 | a6 = {'Name': 'HEART 6', 'Image': 'http://images.com/heart6.jpg'} 314 | a7 = {'Name': 'HEART 7', 'Image': 'http://images.com/heart7.jpg'} 315 | a8 = {'Name': 'HEART 8', 'Image': 'http://images.com/heart8.jpg'} 316 | a9 = {'Name': 'HEART 9', 'Image': 'http://images.com/heart9.jpg'} 317 | a10 = {'Name': 'HEART 10', 'Image': 'http://images.com/heart10.jpg'} 318 | a11 = {'Name': 'HEART J', 'Image': 'http://images.com/heartj.jpg'} 319 | a12 = {'Name': 'HEART Q', 'Image': 'http://images.com/heartq.jpg'} 320 | a13 = {'Name': 'HEART K', 'Image': 'http://images.com/heartk.jpg'} 321 | 322 | cards = [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13] 323 | for card in cards: 324 | if createOneToken(card['Name'], card['Image'], 'CARD') != True: 325 | raise Exception('_createMultiToken failed') 326 | # Notify(["222_createMultiTokens ends"]) 327 | return True 328 | 329 | 330 | def createOneToken(name, url, type): 331 | ''' 332 | create a new token 333 | :param name: 334 | :param url: 335 | :param type: 336 | :return: 337 | ''' 338 | # Notify(["111_createOneToken begins"]) 339 | # generate tokenID 340 | timestamp = GetTime() 341 | totalSupply = Get(ctx, TOTAL_SUPPLY) 342 | newTotalSupply = totalSupply + 1 343 | Put(ctx, TOTAL_SUPPLY, newTotalSupply) 344 | tmp = concatkey(concatkey(selfAddr, timestamp), newTotalSupply) 345 | tokenID = sha256(tmp) 346 | # construct token map 347 | token = {'ID': tokenID, 'Name': name, 'Image': url, 'Type': type} 348 | Notify(["111_createOneToken", newTotalSupply, tokenID, concatkey(TOKEN_ID_PREFIX, tokenID)]) 349 | Put(ctx, concatkey(TOKEN_INDEX_PREFIX, newTotalSupply), tokenID) 350 | ownerKey = concatkey(OWNER_OF_TOKEN_PREFIX, tokenID) 351 | Put(ctx, ownerKey, admin) 352 | Put(ctx, concatkey(TOKEN_ID_PREFIX, tokenID), Serialize(token)) 353 | # add to adminBalance 354 | adminBalance = Get(ctx, concatkey(OWNER_BALANCE_PREFIX, admin)) 355 | Put(ctx, concatkey(OWNER_BALANCE_PREFIX, admin), adminBalance + 1) 356 | # Notify(["333_createOneToken ends"]) 357 | return True 358 | #################### For testing usage only ends ###################### 359 | -------------------------------------------------------------------------------- /OEP8Sample/OEP8Sample.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is a sample of OEP-8 smart contract 3 | """ 4 | from boa.interop.System.Storage import GetContext, Get, Put, Delete 5 | from boa.interop.System.Runtime import CheckWitness, Notify 6 | from boa.interop.System.Action import RegisterAction 7 | from boa.builtins import ToScriptHash, concat 8 | 9 | """ 10 | https://github.com/ONT-Avocados/python-template/blob/master/libs/Utils.py 11 | """ 12 | def Revert(): 13 | """ 14 | Revert the transaction. The opcodes of this function is `09f7f6f5f4f3f2f1f000f0`, 15 | but it will be changed to `ffffffffffffffffffffff` since opcode THROW doesn't 16 | work, so, revert by calling unused opcode. 17 | """ 18 | raise Exception(0xF1F1F2F2F3F3F4F4) 19 | 20 | 21 | """ 22 | https://github.com/ONT-Avocados/python-template/blob/master/libs/SafeCheck.py 23 | """ 24 | def Require(condition): 25 | """ 26 | If condition is not satisfied, return false 27 | :param condition: required condition 28 | :return: True or false 29 | """ 30 | if not condition: 31 | Revert() 32 | return True 33 | 34 | def RequireScriptHash(key): 35 | """ 36 | Checks the bytearray parameter is script hash or not. Script Hash 37 | length should be equal to 20. 38 | :param key: bytearray parameter to check script hash format. 39 | :return: True if script hash or revert the transaction. 40 | """ 41 | Require(len(key) == 20) 42 | return True 43 | 44 | def RequireWitness(witness): 45 | """ 46 | Checks the transaction sender is equal to the witness. If not 47 | satisfying, revert the transaction. 48 | :param witness: required transaction sender 49 | :return: True if transaction sender or revert the transaction. 50 | """ 51 | Require(CheckWitness(witness)) 52 | return True 53 | 54 | """ 55 | https://github.com/ONT-Avocados/python-template/blob/master/libs/SafeMath.py 56 | """ 57 | 58 | def Add(a, b): 59 | """ 60 | Adds two numbers, throws on overflow. 61 | """ 62 | c = a + b 63 | Require(c >= a) 64 | return c 65 | 66 | def Sub(a, b): 67 | """ 68 | Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). 69 | :param a: operand a 70 | :param b: operand b 71 | :return: a - b if a - b > 0 or revert the transaction. 72 | """ 73 | Require(a>=b) 74 | return a-b 75 | 76 | def Mul(a, b): 77 | """ 78 | Multiplies two numbers, throws on overflow. 79 | :param a: operand a 80 | :param b: operand b 81 | :return: a - b if a - b > 0 or revert the transaction. 82 | """ 83 | if a == 0: 84 | return 0 85 | c = a * b 86 | Require(c / a == b) 87 | return c 88 | 89 | def Div(a, b): 90 | """ 91 | Integer division of two numbers, truncating the quotient. 92 | """ 93 | Require(b > 0) 94 | c = a / b 95 | return c 96 | 97 | 98 | TransferEvent = RegisterAction("transfer", "fromAcct", "toAcct", "tokenId", "amount") 99 | ApprovalEvent = RegisterAction("approval", "owner", "spender", "tokenId", "amount") 100 | 101 | # modify to the admin address 102 | admin = ToScriptHash('XXXX') 103 | 104 | # TOKEN_ID1 is used to identify different tokens, to help store the token name, token symbol and balance 105 | TOKEN_ID_LIST = [b'\x01', b'\x02', b'\x03', b'\x04', b'\x05'] 106 | 107 | # TOKEN_ID + NAME --- to store the name of the TOKEN_ID token 108 | NAME = 'Name' 109 | # TOKEN_ID + SYMBOL --- to store the symbol of the TOKEN_ID token 110 | SYMBOL = 'Symbol' 111 | # TOKEN_ID+ BALANCE + address --- to store the balance of address in terms of the TOKEN_ID token 112 | BALANCE = 'Balance' 113 | # TOKEN_ID + TOTAL_SUPPLY --- to store the total supply of the TOKEN_ID token 114 | TOTAL_SUPPLY = 'TotalSupply' 115 | # TOKEN_ID + APPROVE + owner + spender -- to store the approved TOKEN_ID amount to the spender by the owner 116 | APPROVE = 'Approve' 117 | # INITED --- to store "TRUE" in order to make sure this contract can only be deployed once 118 | INITED = 'Initialized' 119 | 120 | 121 | def Main(operation, args): 122 | if operation == "name": 123 | if len(args) != 1: 124 | return False 125 | tokenId = args[0] 126 | return name(tokenId) 127 | if operation == "symbol": 128 | if len(args) != 1: 129 | return False 130 | tokenId = args[0] 131 | return symbol(tokenId) 132 | if operation == "totalSupply": 133 | if len(args) != 1: 134 | return False 135 | tokenId = args[0] 136 | return totalSupply(tokenId) 137 | if operation == "balanceOf": 138 | if len(args) != 2: 139 | return False 140 | account = args[0] 141 | tokenId = args[1] 142 | return balanceOf(account, tokenId) 143 | if operation == "transfer": 144 | if len(args) != 4: 145 | return False 146 | fromAcct = args[0] 147 | toAcct = args[1] 148 | tokenId = args[2] 149 | amount = args[3] 150 | return transfer(fromAcct, toAcct, tokenId, amount) 151 | if operation == "transferMulti": 152 | return transferMulti(args) 153 | if operation == "approve": 154 | if len(args) != 4: 155 | return False 156 | owner = args[0] 157 | spender = args[1] 158 | tokenId = args[2] 159 | amount = args[3] 160 | return approve(owner, spender, tokenId, amount) 161 | if operation == "approveMulti": 162 | return approveMulti(args) 163 | if operation == "allowance": 164 | if len(args) != 3: 165 | return False 166 | owner = args[0] 167 | spender = args[1] 168 | tokenId = args[2] 169 | return allowance(owner, spender, tokenId) 170 | if operation == "transferFrom": 171 | if len(args) != 5: 172 | return False 173 | spender = args[0] 174 | fromAcct = args[1] 175 | toAcct = args[2] 176 | tokenId = args[3] 177 | amount = args[4] 178 | return transferFrom(spender, fromAcct, toAcct, tokenId, amount) 179 | if operation == "transferFromMulti": 180 | return transferFromMulti(args) 181 | ####################### Optional methods begin ######################## 182 | # init() should be invoked first after the contract is deployed 183 | if operation == "init": 184 | if len(args) != 0: 185 | return False 186 | return init() 187 | if operation == "balancesOf": 188 | if len(args) != 1: 189 | return False 190 | account = args[0] 191 | return balancesOf(account) 192 | if operation == "totalBalanceOf": 193 | if len(args) != 1: 194 | return False 195 | account = args[0] 196 | return totalBalanceOf(account) 197 | # only admin can mint token 198 | if operation == "mint": 199 | if len(args) != 2: 200 | return False 201 | tokenId = args[0] 202 | tokenAmount = args[1] 203 | return mint(tokenId, tokenAmount) 204 | ####################### Optional methods end ######################## 205 | return False 206 | 207 | 208 | def name(tokenId): 209 | """ 210 | :param tokenId: helps to format name key = tokenId + NAME 211 | :return: name of the token with tokenId 212 | """ 213 | return Get(GetContext(), concatkey(tokenId, NAME)) 214 | 215 | 216 | def symbol(tokenId): 217 | """ 218 | :param tokenId: helps to format symbol key = tokenId + SYMBOL 219 | :return: symbol of token with tokenId 220 | """ 221 | return Get(GetContext(), concatkey(tokenId, SYMBOL)) 222 | 223 | 224 | def totalSupply(tokenId): 225 | """ 226 | :param tokenId: helps to format totalSupply key = tokenId + TOTAL_SUPPLY 227 | :return: total supply of token with tokenId 228 | """ 229 | return Get(GetContext(), concatkey(tokenId, TOTAL_SUPPLY)) 230 | 231 | 232 | def balanceOf(acct, tokenId): 233 | """ 234 | get balance of accout in terms of token with the tokenId 235 | :param acct: used to check the acct balance 236 | :param tokenId: the tokenId determines which token balance of acct needs to be checked 237 | :return: the balance of acct in terms of tokenId tokens 238 | """ 239 | return Get(GetContext(), concatkey(concatkey(tokenId, BALANCE), acct)) 240 | 241 | 242 | def transfer(fromAcct, toAcct, tokenId, amount): 243 | """ 244 | transfer amount of tokens in terms of tokenId token from fromAcct to the toAcct 245 | :param fromAcct: 246 | :param toAcct: 247 | :param tokenId: 248 | :param amount: 249 | :return: 250 | """ 251 | RequireWitness(fromAcct) 252 | Require(checkTokenId(tokenId)) 253 | RequireScriptHash(fromAcct) 254 | RequireScriptHash(toAcct) 255 | 256 | balanceKey = concatkey(tokenId, BALANCE) 257 | fromKey = concatkey(balanceKey, fromAcct) 258 | fromBalance = Get(GetContext(), fromKey) 259 | if amount > fromBalance or amount <= 0: 260 | return False 261 | if amount == fromBalance: 262 | Delete(GetContext(), fromKey) 263 | else: 264 | Put(GetContext(), fromKey, Sub(fromBalance, amount)) 265 | 266 | toKey = concatkey(balanceKey, toAcct) 267 | toBalance = Get(GetContext(), toKey) 268 | Put(GetContext(), toKey, Add(toBalance, amount)) 269 | 270 | TransferEvent(fromAcct, toAcct, tokenId, amount) 271 | 272 | return True 273 | 274 | 275 | def transferMulti(args): 276 | """ 277 | multi transfer 278 | :param args:[[fromAccount1, toAccount1, tokenId1, amount1],[fromAccount2, toAccount2, tokenId2, amount2]] 279 | :return: True or raise exception 280 | """ 281 | for p in args: 282 | if len(p) != 4: 283 | raise Exception('transferMulti failed - input error!') 284 | if transfer(p[0], p[1], p[2], p[3]) == False: 285 | raise Exception('transferMulti failed - transfer error!') 286 | return True 287 | 288 | 289 | def approve(owner, spender, tokenId, amount): 290 | """ 291 | approve amount of the tokenId token to toAcct address, it can overwrite older approved amount 292 | :param owner: 293 | :param spender: 294 | :param tokenId: 295 | :param amount: 296 | :return: 297 | """ 298 | res = int(owner) 299 | RequireWitness(owner) 300 | RequireScriptHash(owner) 301 | RequireScriptHash(spender) 302 | Require(checkTokenId(tokenId)) 303 | 304 | ownerBalance = balanceOf(owner, tokenId) 305 | # you can use "if" to notify the corresponding message, or use Require to raise exception 306 | Require(ownerBalance >= amount) 307 | Require(amount > 0) 308 | key = concatkey(concatkey(concatkey(tokenId, APPROVE), owner), spender) 309 | Put(GetContext(), key, amount) 310 | 311 | ApprovalEvent(owner, spender, tokenId, amount) 312 | 313 | return True 314 | 315 | 316 | def approveMulti(args): 317 | """ 318 | multi approve 319 | :param args: args:[[owner1, spender1, tokenId1, amount1],[owner2, spender2, tokenId2, amount2]] 320 | :return: 321 | """ 322 | for p in args: 323 | if len(p) != 4: 324 | raise Exception('approveMulti failed - input error!') 325 | if approve(p[0], p[1], p[2], p[3]) == False: 326 | raise Exception('approveMulti failed - approve error!') 327 | return True 328 | 329 | 330 | def allowance(owner, spender, tokenId): 331 | """ 332 | :param owner: 333 | :param spender: 334 | :param tokenId: 335 | :return: 336 | """ 337 | key = concatkey(concatkey(concatkey(tokenId, APPROVE), owner), spender) 338 | return Get(GetContext(), key) 339 | 340 | 341 | def transferFrom(spender, fromAcct, toAcct, tokenId, amount): 342 | """ 343 | :param tokenId: this tokenId token should be approved by its owner to toAcct 344 | :param toAcct: spender 345 | :param amount: False or True 346 | :return: 347 | """ 348 | RequireWitness(spender) 349 | RequireScriptHash(spender) 350 | RequireScriptHash(fromAcct) 351 | RequireScriptHash(toAcct) 352 | Require(checkTokenId(tokenId)) 353 | 354 | fromKey = concatkey(concatkey(tokenId, BALANCE), fromAcct) 355 | fromBalance = Get(GetContext(), fromKey) 356 | Require(fromBalance >= amount) 357 | Require(amount > 0) 358 | toKey = concatkey(concatkey(tokenId, BALANCE), toAcct) 359 | 360 | 361 | approvedKey = concatkey(concatkey(concatkey(tokenId, APPROVE), fromAcct), spender) 362 | approvedAmount = Get(GetContext(), approvedKey) 363 | 364 | if amount > approvedAmount: 365 | raise Exception('you are not allowed to withdraw too many tokens') 366 | elif amount == approvedAmount: 367 | Delete(GetContext(), approvedKey) 368 | Put(GetContext(), fromKey, Sub(fromBalance, amount)) 369 | else: 370 | Put(GetContext(), approvedKey, Sub(approvedAmount, amount)) 371 | Put(GetContext(), fromKey, Sub(fromBalance, amount)) 372 | 373 | toBalance = Get(GetContext(), toKey) 374 | Put(GetContext(), toKey, Add(toBalance, amount)) 375 | 376 | TransferEvent(fromAcct, toAcct, tokenId, amount) 377 | 378 | return True 379 | 380 | 381 | def transferFromMulti(args): 382 | """ 383 | multiple transferFrom 384 | :param args: args:[[spender1, fromAcct1, toAcct1, tokenId1, amount1],[spender2, fromAcct2, toAcct2, tokenId2, amount2]] 385 | :return: 386 | """ 387 | for p in args: 388 | if len(p) != 5: 389 | raise Exception('transferFromMulti failed - input error!') 390 | if transferFrom(p[0], p[1], p[2], p[3], p[4]) == False: 391 | raise Exception('transferFromMulti failed - transfer error!') 392 | return True 393 | 394 | 395 | def concatkey(str1, str2): 396 | return concat(concat(str1, '_'), str2) 397 | 398 | 399 | #################### Optional methods defination starts ###################### 400 | def init(): 401 | ''' 402 | based on your requirements, initialize the tokens 403 | Only admin can init the contract 404 | :return: 405 | ''' 406 | RequireWitness(admin) 407 | if not Get(GetContext(), INITED): 408 | tt = createMultiTypeToken() 409 | if tt == True: 410 | Put(GetContext(), INITED, 'TRUE') 411 | return True 412 | raise Exception("init error") 413 | 414 | return False 415 | 416 | def mint(tokenId, tokenAmount): 417 | """ 418 | only admin can mint token 419 | :param tokenId: 420 | :param tokenAmount: 421 | :return: 422 | """ 423 | Require(CheckWitness(admin)) 424 | Require(checkTokenId(tokenId)) 425 | oldTokenSupply = totalSupply(tokenId) 426 | Require(tokenAmount > 0) 427 | newTokenSupply = Add(tokenAmount, oldTokenSupply) 428 | 429 | Put(GetContext(), concatkey(tokenId, TOTAL_SUPPLY), newTokenSupply) 430 | 431 | 432 | Put(GetContext(), concatkey(concatkey(tokenId, BALANCE), admin), Add(tokenAmount, balanceOf(admin, tokenId))) 433 | TransferEvent('', admin, tokenId, tokenAmount) 434 | 435 | return True 436 | 437 | def createMultiTypeToken(): 438 | Index = [0, 1, 2, 3, 4] 439 | tokenNameList = ['TokenNameFirst', 'TokenNameSecond', 'TokenNameThird', 'TokenNameFourth', 'TokenNameFifth'] 440 | tokenSymbolList = ['TNF', 'TNS', 'TNH', 'TNO', 'TNI'] 441 | tokenSupplyList = [100000, 200000, 300000, 400000, 500000] 442 | 443 | for index in Index: 444 | # get name, symbol, totalsupply 445 | tokenName = tokenNameList[index] 446 | tokenSymbol = tokenSymbolList[index] 447 | tokenTotalSupply = tokenSupplyList[index] 448 | 449 | tokenId = TOKEN_ID_LIST[index] 450 | 451 | # initiate token name 452 | Put(GetContext(), concatkey(tokenId, NAME), tokenName) 453 | # initiate token symbol 454 | Put(GetContext(), concatkey(tokenId, SYMBOL), tokenSymbol) 455 | # initiate token totalSupply 456 | Put(GetContext(), concatkey(tokenId, TOTAL_SUPPLY), tokenTotalSupply) 457 | # transfer all the tokens to admin 458 | Put(GetContext(), concatkey(concatkey(tokenId, BALANCE), admin), tokenTotalSupply) 459 | TransferEvent('', admin, tokenId, tokenTotalSupply) 460 | 461 | return True 462 | 463 | def totalBalanceOf(account): 464 | """ 465 | :param account: 466 | :return: the total balance of 5 type of tokens 467 | """ 468 | Index = [0, 1, 2, 3, 4] 469 | totalBalance = 0 470 | for index in Index: 471 | tokenId = TOKEN_ID_LIST[index] 472 | totalBalance = Add(totalBalance, Get(GetContext(), concatkey(concatkey(tokenId, BALANCE), account))) 473 | return totalBalance 474 | 475 | 476 | def balancesOf(account): 477 | """ 478 | :param account: 479 | :return: the different balances of account 480 | """ 481 | Index = [0, 1, 2, 3, 4] 482 | balancesList = [] 483 | for index in Index: 484 | tokenId = TOKEN_ID_LIST[index] 485 | balancesList.append(Get(GetContext(), concatkey(concatkey(tokenId, BALANCE), account))) 486 | return balancesList 487 | 488 | def checkTokenId(tokenId): 489 | # here we check if the tokenId is legal with the help of getting its name 490 | if Get(GetContext(), concatkey(tokenId, NAME)): 491 | return True 492 | else: 493 | return False 494 | #################### Optional methods defination starts ###################### -------------------------------------------------------------------------------- /OEP8Sample/OEP8Sample_compiler2.0.py: -------------------------------------------------------------------------------- 1 | OntCversion = '2.0.0' 2 | """ 3 | This is a sample of OEP-8 smart contract 4 | """ 5 | from ontology.interop.System.Storage import GetContext, Get, Put, Delete 6 | from ontology.interop.System.Runtime import CheckWitness, Notify 7 | from ontology.interop.System.Action import RegisterAction 8 | from ontology.builtins import concat 9 | from ontology.interop.Ontology.Runtime import Base58ToAddress 10 | 11 | """ 12 | https://github.com/ONT-Avocados/python-template/blob/master/libs/Utils.py 13 | """ 14 | def Revert(): 15 | """ 16 | Revert the transaction. The opcodes of this function is `09f7f6f5f4f3f2f1f000f0`, 17 | but it will be changed to `ffffffffffffffffffffff` since opcode THROW doesn't 18 | work, so, revert by calling unused opcode. 19 | """ 20 | raise Exception(0xF1F1F2F2F3F3F4F4) 21 | 22 | 23 | """ 24 | https://github.com/ONT-Avocados/python-template/blob/master/libs/SafeCheck.py 25 | """ 26 | def Require(condition): 27 | """ 28 | If condition is not satisfied, return false 29 | :param condition: required condition 30 | :return: True or false 31 | """ 32 | if not condition: 33 | Revert() 34 | return True 35 | 36 | def RequireScriptHash(key): 37 | """ 38 | Checks the bytearray parameter is script hash or not. Script Hash 39 | length should be equal to 20. 40 | :param key: bytearray parameter to check script hash format. 41 | :return: True if script hash or revert the transaction. 42 | """ 43 | Require(len(key) == 20) 44 | return True 45 | 46 | def RequireWitness(witness): 47 | """ 48 | Checks the transaction sender is equal to the witness. If not 49 | satisfying, revert the transaction. 50 | :param witness: required transaction sender 51 | :return: True if transaction sender or revert the transaction. 52 | """ 53 | Require(CheckWitness(witness)) 54 | return True 55 | 56 | """ 57 | https://github.com/ONT-Avocados/python-template/blob/master/libs/SafeMath.py 58 | """ 59 | 60 | def Add(a, b): 61 | """ 62 | Adds two numbers, throws on overflow. 63 | """ 64 | c = a + b 65 | Require(c >= a) 66 | return c 67 | 68 | def Sub(a, b): 69 | """ 70 | Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). 71 | :param a: operand a 72 | :param b: operand b 73 | :return: a - b if a - b > 0 or revert the transaction. 74 | """ 75 | Require(a>=b) 76 | return a-b 77 | 78 | def Mul(a, b): 79 | """ 80 | Multiplies two numbers, throws on overflow. 81 | :param a: operand a 82 | :param b: operand b 83 | :return: a - b if a - b > 0 or revert the transaction. 84 | """ 85 | if a == 0: 86 | return 0 87 | c = a * b 88 | Require(c / a == b) 89 | return c 90 | 91 | def Div(a, b): 92 | """ 93 | Integer division of two numbers, truncating the quotient. 94 | """ 95 | Require(b > 0) 96 | c = a / b 97 | return c 98 | 99 | 100 | TransferEvent = RegisterAction("transfer", "fromAcct", "toAcct", "tokenId", "amount") 101 | ApprovalEvent = RegisterAction("approval", "owner", "spender", "tokenId", "amount") 102 | 103 | # modify to the admin address 104 | admin = Base58ToAddress('XXXX') 105 | 106 | # TOKEN_ID1 is used to identify different tokens, to help store the token name, token symbol and balance 107 | TOKEN_ID_LIST = [b'\x01', b'\x02', b'\x03', b'\x04', b'\x05'] 108 | 109 | # TOKEN_ID + NAME --- to store the name of the TOKEN_ID token 110 | NAME = 'Name' 111 | # TOKEN_ID + SYMBOL --- to store the symbol of the TOKEN_ID token 112 | SYMBOL = 'Symbol' 113 | # TOKEN_ID+ BALANCE + address --- to store the balance of address in terms of the TOKEN_ID token 114 | BALANCE = 'Balance' 115 | # TOKEN_ID + TOTAL_SUPPLY --- to store the total supply of the TOKEN_ID token 116 | TOTAL_SUPPLY = 'TotalSupply' 117 | # TOKEN_ID + APPROVE + owner + spender -- to store the approved TOKEN_ID amount to the spender by the owner 118 | APPROVE = 'Approve' 119 | # INITED --- to store "TRUE" in order to make sure this contract can only be deployed once 120 | INITED = 'Initialized' 121 | 122 | 123 | def Main(operation, args): 124 | if operation == "name": 125 | if len(args) != 1: 126 | return False 127 | tokenId = args[0] 128 | return name(tokenId) 129 | if operation == "symbol": 130 | if len(args) != 1: 131 | return False 132 | tokenId = args[0] 133 | return symbol(tokenId) 134 | if operation == "totalSupply": 135 | if len(args) != 1: 136 | return False 137 | tokenId = args[0] 138 | return totalSupply(tokenId) 139 | if operation == "balanceOf": 140 | if len(args) != 2: 141 | return False 142 | account = args[0] 143 | tokenId = args[1] 144 | return balanceOf(account, tokenId) 145 | if operation == "transfer": 146 | if len(args) != 4: 147 | return False 148 | fromAcct = args[0] 149 | toAcct = args[1] 150 | tokenId = args[2] 151 | amount = args[3] 152 | return transfer(fromAcct, toAcct, tokenId, amount) 153 | if operation == "transferMulti": 154 | return transferMulti(args) 155 | if operation == "approve": 156 | if len(args) != 4: 157 | return False 158 | owner = args[0] 159 | spender = args[1] 160 | tokenId = args[2] 161 | amount = args[3] 162 | return approve(owner, spender, tokenId, amount) 163 | if operation == "approveMulti": 164 | return approveMulti(args) 165 | if operation == "allowance": 166 | if len(args) != 3: 167 | return False 168 | owner = args[0] 169 | spender = args[1] 170 | tokenId = args[2] 171 | return allowance(owner, spender, tokenId) 172 | if operation == "transferFrom": 173 | if len(args) != 5: 174 | return False 175 | spender = args[0] 176 | fromAcct = args[1] 177 | toAcct = args[2] 178 | tokenId = args[3] 179 | amount = args[4] 180 | return transferFrom(spender, fromAcct, toAcct, tokenId, amount) 181 | if operation == "transferFromMulti": 182 | return transferFromMulti(args) 183 | ####################### Optional methods begin ######################## 184 | # init() should be invoked first after the contract is deployed 185 | if operation == "init": 186 | if len(args) != 0: 187 | return False 188 | return init() 189 | if operation == "balancesOf": 190 | if len(args) != 1: 191 | return False 192 | account = args[0] 193 | return balancesOf(account) 194 | if operation == "totalBalanceOf": 195 | if len(args) != 1: 196 | return False 197 | account = args[0] 198 | return totalBalanceOf(account) 199 | # only admin can mint token 200 | if operation == "mint": 201 | if len(args) != 2: 202 | return False 203 | tokenId = args[0] 204 | tokenAmount = args[1] 205 | return mint(tokenId, tokenAmount) 206 | ####################### Optional methods end ######################## 207 | return False 208 | 209 | 210 | def name(tokenId): 211 | """ 212 | :param tokenId: helps to format name key = tokenId + NAME 213 | :return: name of the token with tokenId 214 | """ 215 | return Get(GetContext(), concatkey(tokenId, NAME)) 216 | 217 | 218 | def symbol(tokenId): 219 | """ 220 | :param tokenId: helps to format symbol key = tokenId + SYMBOL 221 | :return: symbol of token with tokenId 222 | """ 223 | return Get(GetContext(), concatkey(tokenId, SYMBOL)) 224 | 225 | 226 | def totalSupply(tokenId): 227 | """ 228 | :param tokenId: helps to format totalSupply key = tokenId + TOTAL_SUPPLY 229 | :return: total supply of token with tokenId 230 | """ 231 | return Get(GetContext(), concatkey(tokenId, TOTAL_SUPPLY)) 232 | 233 | 234 | def balanceOf(acct, tokenId): 235 | """ 236 | get balance of accout in terms of token with the tokenId 237 | :param acct: used to check the acct balance 238 | :param tokenId: the tokenId determines which token balance of acct needs to be checked 239 | :return: the balance of acct in terms of tokenId tokens 240 | """ 241 | return Get(GetContext(), concatkey(concatkey(tokenId, BALANCE), acct)) 242 | 243 | 244 | def transfer(fromAcct, toAcct, tokenId, amount): 245 | """ 246 | transfer amount of tokens in terms of tokenId token from fromAcct to the toAcct 247 | :param fromAcct: 248 | :param toAcct: 249 | :param tokenId: 250 | :param amount: 251 | :return: 252 | """ 253 | RequireWitness(fromAcct) 254 | Require(checkTokenId(tokenId)) 255 | RequireScriptHash(fromAcct) 256 | RequireScriptHash(toAcct) 257 | 258 | balanceKey = concatkey(tokenId, BALANCE) 259 | fromKey = concatkey(balanceKey, fromAcct) 260 | fromBalance = Get(GetContext(), fromKey) 261 | if amount > fromBalance or amount <= 0: 262 | return False 263 | if amount == fromBalance: 264 | Delete(GetContext(), fromKey) 265 | else: 266 | Put(GetContext(), fromKey, Sub(fromBalance, amount)) 267 | 268 | toKey = concatkey(balanceKey, toAcct) 269 | toBalance = Get(GetContext(), toKey) 270 | Put(GetContext(), toKey, Add(toBalance, amount)) 271 | 272 | TransferEvent(fromAcct, toAcct, tokenId, amount) 273 | 274 | return True 275 | 276 | 277 | def transferMulti(args): 278 | """ 279 | multi transfer 280 | :param args:[[fromAccount1, toAccount1, tokenId1, amount1],[fromAccount2, toAccount2, tokenId2, amount2]] 281 | :return: True or raise exception 282 | """ 283 | for p in args: 284 | if len(p) != 4: 285 | raise Exception('transferMulti failed - input error!') 286 | if transfer(p[0], p[1], p[2], p[3]) == False: 287 | raise Exception('transferMulti failed - transfer error!') 288 | return True 289 | 290 | 291 | def approve(owner, spender, tokenId, amount): 292 | """ 293 | approve amount of the tokenId token to toAcct address, it can overwrite older approved amount 294 | :param owner: 295 | :param spender: 296 | :param tokenId: 297 | :param amount: 298 | :return: 299 | """ 300 | res = int(owner) 301 | RequireWitness(owner) 302 | RequireScriptHash(owner) 303 | RequireScriptHash(spender) 304 | Require(checkTokenId(tokenId)) 305 | 306 | ownerBalance = balanceOf(owner, tokenId) 307 | # you can use "if" to notify the corresponding message, or use Require to raise exception 308 | Require(ownerBalance >= amount) 309 | Require(amount > 0) 310 | key = concatkey(concatkey(concatkey(tokenId, APPROVE), owner), spender) 311 | Put(GetContext(), key, amount) 312 | 313 | ApprovalEvent(owner, spender, tokenId, amount) 314 | 315 | return True 316 | 317 | 318 | def approveMulti(args): 319 | """ 320 | multi approve 321 | :param args: args:[[owner1, spender1, tokenId1, amount1],[owner2, spender2, tokenId2, amount2]] 322 | :return: 323 | """ 324 | for p in args: 325 | if len(p) != 4: 326 | raise Exception('approveMulti failed - input error!') 327 | if approve(p[0], p[1], p[2], p[3]) == False: 328 | raise Exception('approveMulti failed - approve error!') 329 | return True 330 | 331 | 332 | def allowance(owner, spender, tokenId): 333 | """ 334 | :param owner: 335 | :param spender: 336 | :param tokenId: 337 | :return: 338 | """ 339 | key = concatkey(concatkey(concatkey(tokenId, APPROVE), owner), spender) 340 | return Get(GetContext(), key) 341 | 342 | 343 | def transferFrom(spender, fromAcct, toAcct, tokenId, amount): 344 | """ 345 | :param tokenId: this tokenId token should be approved by its owner to toAcct 346 | :param toAcct: spender 347 | :param amount: False or True 348 | :return: 349 | """ 350 | RequireWitness(spender) 351 | RequireScriptHash(spender) 352 | RequireScriptHash(fromAcct) 353 | RequireScriptHash(toAcct) 354 | Require(checkTokenId(tokenId)) 355 | 356 | fromKey = concatkey(concatkey(tokenId, BALANCE), fromAcct) 357 | fromBalance = Get(GetContext(), fromKey) 358 | Require(fromBalance >= amount) 359 | Require(amount > 0) 360 | toKey = concatkey(concatkey(tokenId, BALANCE), toAcct) 361 | 362 | 363 | approvedKey = concatkey(concatkey(concatkey(tokenId, APPROVE), fromAcct), spender) 364 | approvedAmount = Get(GetContext(), approvedKey) 365 | 366 | if amount > approvedAmount: 367 | raise Exception('you are not allowed to withdraw too many tokens') 368 | elif amount == approvedAmount: 369 | Delete(GetContext(), approvedKey) 370 | Put(GetContext(), fromKey, Sub(fromBalance, amount)) 371 | else: 372 | Put(GetContext(), approvedKey, Sub(approvedAmount, amount)) 373 | Put(GetContext(), fromKey, Sub(fromBalance, amount)) 374 | 375 | toBalance = Get(GetContext(), toKey) 376 | Put(GetContext(), toKey, Add(toBalance, amount)) 377 | 378 | TransferEvent(fromAcct, toAcct, tokenId, amount) 379 | 380 | return True 381 | 382 | 383 | def transferFromMulti(args): 384 | """ 385 | multiple transferFrom 386 | :param args: args:[[spender1, fromAcct1, toAcct1, tokenId1, amount1],[spender2, fromAcct2, toAcct2, tokenId2, amount2]] 387 | :return: 388 | """ 389 | for p in args: 390 | if len(p) != 5: 391 | raise Exception('transferFromMulti failed - input error!') 392 | if transferFrom(p[0], p[1], p[2], p[3], p[4]) == False: 393 | raise Exception('transferFromMulti failed - transfer error!') 394 | return True 395 | 396 | 397 | def concatkey(str1, str2): 398 | return concat(concat(str1, '_'), str2) 399 | 400 | 401 | #################### Optional methods defination starts ###################### 402 | def init(): 403 | ''' 404 | based on your requirements, initialize the tokens 405 | Only admin can init the contract 406 | :return: 407 | ''' 408 | RequireWitness(admin) 409 | if not Get(GetContext(), INITED): 410 | tt = createMultiTypeToken() 411 | if tt == True: 412 | Put(GetContext(), INITED, 'TRUE') 413 | return True 414 | raise Exception("init error") 415 | 416 | return False 417 | 418 | def mint(tokenId, tokenAmount): 419 | """ 420 | only admin can mint token 421 | :param tokenId: 422 | :param tokenAmount: 423 | :return: 424 | """ 425 | Require(CheckWitness(admin)) 426 | Require(checkTokenId(tokenId)) 427 | oldTokenSupply = totalSupply(tokenId) 428 | Require(tokenAmount > 0) 429 | newTokenSupply = Add(tokenAmount, oldTokenSupply) 430 | 431 | Put(GetContext(), concatkey(tokenId, TOTAL_SUPPLY), newTokenSupply) 432 | 433 | 434 | Put(GetContext(), concatkey(concatkey(tokenId, BALANCE), admin), Add(tokenAmount, balanceOf(admin, tokenId))) 435 | TransferEvent('', admin, tokenId, tokenAmount) 436 | 437 | return True 438 | 439 | def createMultiTypeToken(): 440 | Index = [0, 1, 2, 3, 4] 441 | tokenNameList = ['TokenNameFirst', 'TokenNameSecond', 'TokenNameThird', 'TokenNameFourth', 'TokenNameFifth'] 442 | tokenSymbolList = ['TNF', 'TNS', 'TNH', 'TNO', 'TNI'] 443 | tokenSupplyList = [100000, 200000, 300000, 400000, 500000] 444 | 445 | for index in Index: 446 | # get name, symbol, totalsupply 447 | tokenName = tokenNameList[index] 448 | tokenSymbol = tokenSymbolList[index] 449 | tokenTotalSupply = tokenSupplyList[index] 450 | 451 | tokenId = TOKEN_ID_LIST[index] 452 | 453 | # initiate token name 454 | Put(GetContext(), concatkey(tokenId, NAME), tokenName) 455 | # initiate token symbol 456 | Put(GetContext(), concatkey(tokenId, SYMBOL), tokenSymbol) 457 | # initiate token totalSupply 458 | Put(GetContext(), concatkey(tokenId, TOTAL_SUPPLY), tokenTotalSupply) 459 | # transfer all the tokens to admin 460 | Put(GetContext(), concatkey(concatkey(tokenId, BALANCE), admin), tokenTotalSupply) 461 | TransferEvent('', admin, tokenId, tokenTotalSupply) 462 | 463 | return True 464 | 465 | def totalBalanceOf(account): 466 | """ 467 | :param account: 468 | :return: the total balance of 5 type of tokens 469 | """ 470 | Index = [0, 1, 2, 3, 4] 471 | totalBalance = 0 472 | for index in Index: 473 | tokenId = TOKEN_ID_LIST[index] 474 | totalBalance = Add(totalBalance, Get(GetContext(), concatkey(concatkey(tokenId, BALANCE), account))) 475 | return totalBalance 476 | 477 | 478 | def balancesOf(account): 479 | """ 480 | :param account: 481 | :return: the different balances of account 482 | """ 483 | Index = [0, 1, 2, 3, 4] 484 | balancesList = [] 485 | for index in Index: 486 | tokenId = TOKEN_ID_LIST[index] 487 | balancesList.append(Get(GetContext(), concatkey(concatkey(tokenId, BALANCE), account))) 488 | return balancesList 489 | 490 | def checkTokenId(tokenId): 491 | # here we check if the tokenId is legal with the help of getting its name 492 | if Get(GetContext(), concatkey(tokenId, NAME)): 493 | return True 494 | else: 495 | return False 496 | #################### Optional methods defination starts ###################### --------------------------------------------------------------------------------