def _deploy(data: Any, upgrade: bool): """ The contracts initial entry point, on deployment. """ if upgrade: return if get(DEPLOYED).to_bool(): abort() tx = cast(Transaction, script_container) #DEBUG_START #custom owner for tests if tx.sender is None: owner = UInt160(b'\x96Wl\x0e**\x1c!\xc4\xac^\xbd)31\x15%A\x1f@') #DEBUG_END put(DEPLOYED, True) put(PAUSED, False) put(TOKEN_COUNT, 0) put(MINT_FEE, MINT_FEE_ON_DEPLOY) auth: List[UInt160] = [] auth.append(tx.sender) serialized = serialize(auth) put(AUTH_ADDRESSES, serialized) wl: List[UInt160] = [] wl.append(tx.sender) wl_serialized = serialize(auth) put(WL_ADDRESSES, wl_serialized)
def onNEP17Payment(from_address: UInt160, amount: int, data: Any): """ NEP-17 affirms :"if the receiver is a deployed contract, the function MUST call onPayment method on receiver contract with the data parameter from transfer AFTER firing the Transfer event. If the receiver doesn't want to receive this transfer it MUST call ABORT." Therefore, since this is a smart contract, onPayment must exists. There is no guideline as to how it should verify the transaction and it's up to the user to make this verification. For instance, this onPayment method checks if this smart contract is receiving NEO or GAS so that it can mint a NEP17 token. If it's not receiving a native token, than it will abort. :param from_address: the address of the one who is trying to send cryptocurrency to this smart contract :type from_address: UInt160 :param amount: the amount of cryptocurrency that is being sent to the this smart contract :type amount: int :param data: any pertinent data that might validate the transaction :type data: Any """ # Use calling_script_hash to identify if the incoming token is NEO or GAS if calling_script_hash == NEO: corresponding_amount = amount * AMOUNT_PER_NEO mint(from_address, corresponding_amount) elif calling_script_hash == GAS: corresponding_amount = amount * AMOUNT_PER_GAS mint(from_address, corresponding_amount) else: abort()
def swap_tokens(amount_in: int, amount_out_min: int, token_in: UInt160, user_address: UInt160) -> int: """ Swaps two tokens with a small fee in the process. :param amount_in: the amount of tokens that the user is trying to swap :type amount_in: int :param amount_out_min: the minimum amount of tokens that the user wants to receive :type amount_out_min: int :param token_in: the address of the token that the user is trying to use in the swap :type token_in: UInt160 :param user_address: the user's address :type user_address: UInt160 :return: the amount of tokens that the user received from the swap :rtype: int """ # this verification exists thanks to a limitation in the TestEngine, it's returning None when using calling_script_hash # using just `user_address = calling_script hash` should be enough if calling_script_hash is not None: # TODO: remove the verification when the TestEngine starts sending a calling script hash user_address = calling_script_hash else: assert check_witness(user_address) assert check_witness(user_address) assert token_in == UInt160(get(TOKEN_A)) or token_in == UInt160(get(TOKEN_B)) # Verifies if the user is trying to swap token_a or token_b and set the variables accordingly if token_in == UInt160(get(TOKEN_A)): reserve_token_in = get(SUPPLY_KEY + TOKEN_A).to_int() reserve_token_out = get(SUPPLY_KEY + TOKEN_B).to_int() amount_token_a_in = amount_in amount_token_b_in = 0 else: reserve_token_in = get(SUPPLY_KEY + TOKEN_B).to_int() reserve_token_out = get(SUPPLY_KEY + TOKEN_A).to_int() amount_token_a_in = 0 amount_token_b_in = amount_in # Calculates the amount of tokens the user will receive amount_in_fee = amount_in * (1000 - FEE) amount_out = amount_in_fee * reserve_token_out // (reserve_token_in * 1000 + amount_in_fee) assert amount_out >= amount_out_min # Checks if the user allowed enough tokens amount_allowed = call_contract(token_in, 'allowance', [user_address, executing_script_hash]) if isinstance(amount_allowed, int): assert amount_allowed >= amount_in else: abort() call_contract(token_in, 'transfer_from', [executing_script_hash, user_address, executing_script_hash, amount_in, None]) if amount_token_a_in != 0: swap(0, amount_out, user_address) else: swap(amount_out, 0, user_address) return amount_out
def onNEP17Payment(from_address: UInt160, amount: int, data: Any): """ This contract is currently not accepting any transfers. :param from_address: the address of the one who is trying to send cryptocurrency to this smart contract :param amount: the amount of cryptocurrency that is being sent to the this smart contract :param data: any pertinent data that might validate the transaction """ abort()
def mint(user_address: UInt160) -> int: """ Mints AMM tokens, this function will be called by `add_liquidity()`. It's best practice to separate `add_liquidity` and `mint` into different contracts, `add_liquidity` should be in a Router, while `mint` should be in another smart contract, however, since this is just an example, they both are in this same smart contract. :param user_address: the address of the user that wants to add liquidity to the pool :type user_address: UInt160 :return: the amount of liquidity tokens that were minted :rtype: int :raise AssertionError: raised if the liquidity ends up being equal or less than 0 """ # reserve_token_a and reserve_token_b are the amount of token_a and token_b tokens that the smart contract has saved in the # storage, it's not the actual amount that is in the balance, because the amount is not updated after transferring # the token_a and token_b tokens, it will be update only after minting reserve_token_a = get(SUPPLY_KEY + TOKEN_A).to_int() reserve_token_b = get(SUPPLY_KEY + TOKEN_B).to_int() # balance_token_a and balance_token_b are the actual amount that are in the balance of this smart contract balance_token_a = call_contract(UInt160(get(TOKEN_A)), 'balanceOf', [executing_script_hash]) balance_token_b = call_contract(UInt160(get(TOKEN_B)), 'balanceOf', [executing_script_hash]) liquidity: int if isinstance(balance_token_a, int) and isinstance(balance_token_b, int): # amount_token_a and amount_token_b are the quantity of tokens that were deposited in the balance of this smart contract amount_token_a = balance_token_a - reserve_token_a amount_token_b = balance_token_b - reserve_token_b total_supply = get(SUPPLY_KEY).to_int() # if there are no AMM tokens, then the quantity of AMM tokens that will be minted are calculated multiplying # amount_token_a and amount_token_b if total_supply == 0: liquidity = sqrt(amount_token_b * amount_token_a) # if the pool is not empty then the amount of AMM tokens that will be minted are calculated the way shown below else: liquidity = min(amount_token_a * total_supply // reserve_token_a, amount_token_b * total_supply // reserve_token_b) assert liquidity > 0 # updates the total supply of AMM tokens put(SUPPLY_KEY, total_supply + liquidity) # change the amount of liquidity the user has put(user_address, get(user_address).to_int() + liquidity) on_transfer(None, user_address, liquidity) update(balance_token_a, balance_token_b) on_mint(user_address, amount_token_a, amount_token_b) else: abort() return liquidity
def burn(liquidity: int, user_address: UInt160) -> List[int]: """ Burns AMM tokens, this function will be called by `remove_liquidity()`. It's best practice to separate `remove_liquidity` and `mint` into different contracts, `add_liquidity` should be in a Router, while `burn` should be in another smart contract, however, since this is just an example, they both are in this same smart contract. :param liquidity: how many AMM tokens will be removed from the pool :type liquidity: int :param user_address: the address of the user that wants to remove liquidity of the pool :type user_address: int :return: at index 0 and 1, the amount of token_a and token_b tokens that were transferred, respectively :rtype: list :raise AssertionError: raised if amount_token_a or amount_token_b is equal or less than zero """ # balance_token_a and balance_token_b are the actual amount that are in the balance of this smart contract balance_token_a = call_contract(UInt160(get(TOKEN_A)), 'balanceOf', [executing_script_hash]) balance_token_b = call_contract(UInt160(get(TOKEN_B)), 'balanceOf', [executing_script_hash]) amount_token_a: int = 0 amount_token_b: int = 0 if isinstance(balance_token_a, int) and isinstance(balance_token_b, int): total_supply = get(SUPPLY_KEY).to_int() # amount_token_a and amount_token_b are the amount that will be transferred to the user after burning the liquidity amount_token_a = liquidity * balance_token_a // total_supply amount_token_b = liquidity * balance_token_b // total_supply assert amount_token_a > 0 and amount_token_b > 0 # changing the user balance after burning the liquidity put(user_address, balanceOf(user_address) - liquidity) # update the amount of AMM tokens in this smart contract put(SUPPLY_KEY, total_supply - liquidity) on_transfer(user_address, None, liquidity) call_contract(UInt160(get(TOKEN_A)), 'transfer', [executing_script_hash, user_address, amount_token_a, None]) call_contract(UInt160(get(TOKEN_B)), 'transfer', [executing_script_hash, user_address, amount_token_b, None]) balance_token_a = call_contract(UInt160(get(TOKEN_A)), 'balanceOf', [executing_script_hash]) balance_token_b = call_contract(UInt160(get(TOKEN_B)), 'balanceOf', [executing_script_hash]) if isinstance(balance_token_a, int) and isinstance(balance_token_b, int): update(balance_token_a, balance_token_b) on_burn(user_address, amount_token_a, amount_token_b) else: abort() else: abort() return [amount_token_a, amount_token_b]
def onNEP17Payment(from_address: UInt160, amount: int, data: Any): """ :param from_address: the address of the one who is trying to send cryptocurrency to this smart contract :type from_address: UInt160 :param amount: the amount of cryptocurrency that is being sent to the this smart contract :type amount: int :param data: any pertinent data that might validate the transaction :type data: Any """ if calling_script_hash != GAS: abort() debug(["onNEP17Payment", data])
def onNEP17Payment(from_address: UInt160, amount: int, data: Any): """ Since this is a deployed contract, transfer will be calling this onPayment method with the data parameter from transfer. If someone is doing a not required transfer, then ABORT will be called. :param from_address: the address of the one who is trying to transfer cryptocurrency to this smart contract :type from_address: UInt160 :param amount: the amount of cryptocurrency that is being sent to this smart contract :type amount: int :param data: any pertinent data that may validate the transaction :type data: Any :raise AssertionError: raised if `from_address` length is not 20 """ # the parameters from and to should be 20-byte addresses. If not, this method should throw an exception. aux_var = from_address is not None # TODO: using identity operators or isinstance as a condition of an if is bugged if aux_var: assert len(from_address) == 20 # this validation will verify if Neo is trying to mint GAS to this smart contract aux_var = from_address is None # TODO: using identity operators or isinstance as a condition of an if is bugged if aux_var and runtime.calling_script_hash == GAS_SCRIPT: return if not storage.get(NOT_INITIALIZED).to_bool(): # Used to check if the one who's transferring to this contract is the PERSON_A address = storage.get(ADDRESS_PREFIX + PERSON_A) # Used to check if PERSON_A already transfer to this smart contract funded_crypto = storage.get(FUNDED_PREFIX + PERSON_A).to_int() # Used to check if PERSON_A is transferring the correct amount amount_crypto = storage.get(AMOUNT_PREFIX + PERSON_A).to_int() # Used to check if PERSON_A is transferring the correct token token_crypto = storage.get(TOKEN_PREFIX + PERSON_A) if (from_address == address and funded_crypto == 0 and amount == amount_crypto and runtime.calling_script_hash == token_crypto): storage.put(FUNDED_PREFIX + PERSON_A, amount) return else: # Used to check if the one who's transferring to this contract is the OTHER_PERSON address = storage.get(ADDRESS_PREFIX + PERSON_B) # Used to check if PERSON_B already transfer to this smart contract funded_crypto = storage.get(FUNDED_PREFIX + PERSON_B).to_int() # Used to check if PERSON_B is transferring the correct amount amount_crypto = storage.get(AMOUNT_PREFIX + PERSON_B).to_int() # Used to check if PERSON_B is transferring the correct token token_crypto = storage.get(TOKEN_PREFIX + PERSON_B) if (from_address == address and funded_crypto == 0 and amount == amount_crypto and runtime.calling_script_hash == token_crypto): storage.put(FUNDED_PREFIX + PERSON_B, amount) return abort()
def onNEP11Payment(from_address: UInt160, amount: int, tokenId: bytes, data: Any): """ :param from_address: the address of the one who is trying to send cryptocurrency to this smart contract :type from_address: UInt160 :param amount: the amount of cryptocurrency that is being sent to the this smart contract :type amount: int :param token: the token hash as bytes :type token: bytes :param data: any pertinent data that might validate the transaction :type data: Any """ abort()
def swap(amount_token_a_out: int, amount_token_b_out: int, user_address: UInt160): """ Swaps one token with another, this function will be called by `swap_tokens`. It's best practice to separate `swap_tokens` and `swap` into different contracts, `swap_tokens` should be in a Router, while `swap` should be in another smart contract, however, since this is just an example, they both are in this same smart contract. :param amount_token_a_out: the amount of token_a that will be given to the user :type amount_token_a_out: int :param amount_token_b_out: the amount of token_b that will be given to the user :type amount_token_b_out: int :param user_address: the user's address :type user_address: UInt160 :raise AssertionError: raised if the amount_token_a_out and amount_token_b_out are equal or less than zero, if the amount the user is going to receive is greater than the amount in the reserve, if the smart contract didn't receive any token from the user, or if the constant k after the swap ends up being lower than the one at the beginning """ assert amount_token_a_out > 0 or amount_token_b_out > 0 reserve_token_a = get(SUPPLY_KEY + TOKEN_A).to_int() reserve_token_b = get(SUPPLY_KEY + TOKEN_B).to_int() assert amount_token_a_out < reserve_token_a and amount_token_b_out < reserve_token_b if amount_token_a_out > 0: call_contract(UInt160(get(TOKEN_A)), 'transfer', [executing_script_hash, user_address, amount_token_a_out, None]) if amount_token_b_out > 0: call_contract(UInt160(get(TOKEN_B)), 'transfer', [executing_script_hash, user_address, amount_token_b_out, None]) # balance_token_a and balance_token_b are the actual amount that are in the balance of this smart contract balance_token_a = call_contract(UInt160(get(TOKEN_A)), 'balanceOf', [executing_script_hash]) balance_token_b = call_contract(UInt160(get(TOKEN_B)), 'balanceOf', [executing_script_hash]) if isinstance(balance_token_a, int) and isinstance(balance_token_b, int): amount_token_a_in = balance_token_a - (reserve_token_a - amount_token_a_out) if balance_token_a > reserve_token_a - amount_token_a_out else 0 amount_token_b_in = balance_token_b - (reserve_token_b - amount_token_b_out) if balance_token_b > reserve_token_b - amount_token_b_out else 0 assert amount_token_a_in > 0 or amount_token_b_in > 0 balance_token_a_adjusted = balance_token_a * 1000 - amount_token_a_in * FEE balance_token_b_adjusted = balance_token_b * 1000 - amount_token_b_in * FEE constant_k_new = balance_token_a_adjusted * balance_token_b_adjusted constant_k_old = reserve_token_a * 1000 * reserve_token_b * 1000 assert constant_k_new >= constant_k_old update(balance_token_a, balance_token_b) on_swap(user_address, amount_token_a_in, amount_token_b_in, amount_token_a_out, amount_token_b_out) else: abort()
def on_nep11_payment(from_address: UInt160, amount: int, token_id: ByteString, data: Any): """ This contract will not receive another NEP-11 token. :param from_address: the address of the one who is trying to send cryptocurrency to this smart contract :type from_address: UInt160 :param amount: the amount of cryptocurrency that is being sent to the this smart contract :type amount: int :param token_id: the id of the token that is being sent :type token_id: ByteString :param data: any pertinent data that might validate the transaction :type data: Any """ abort()
def onNEP17Payment(from_address: UInt160, amount: int, data: Any): """ Since this is a deployed contract, transfer will be calling this onPayment method with the data parameter from transfer. If someone is doing a not required transfer, then ABORT will be called. :param from_address: the address of the one who is trying to transfer cryptocurrency to this smart contract :type from_address: UInt160 :param amount: the amount of cryptocurrency that is being sent to this smart contract :type amount: int :param data: any pertinent data that may validate the transaction :type data: Any :raise AssertionError: raised if `from_address` length is not 20 """ # the parameters from and to should be 20-byte addresses. If not, this method should throw an exception. assert len(from_address) == 20 if not get(NOT_INITIALIZED).to_bool(): # Used to check if the one who's transferring to this contract is the OWNER address = get(ADDRESS_PREFIX + OWNER) # Used to check if OWNER already transfer to this smart contract funded_crypto = get(FUNDED_PREFIX + OWNER).to_int() # Used to check if OWNER is transferring the correct amount amount_crypto = get(AMOUNT_PREFIX + OWNER).to_int() # Used to check if OWNER is transferring the correct token token_crypto = get(TOKEN_PREFIX + OWNER) if (from_address == address and funded_crypto == 0 and amount == amount_crypto and calling_script_hash == token_crypto): put(FUNDED_PREFIX + OWNER, amount) return else: # Used to check if the one who's transferring to this contract is the OTHER_PERSON address = get(ADDRESS_PREFIX + OTHER_PERSON) # Used to check if OTHER_PERSON already transfer to this smart contract funded_crypto = get(FUNDED_PREFIX + OTHER_PERSON).to_int() # Used to check if OTHER_PERSON is transferring the correct amount amount_crypto = get(AMOUNT_PREFIX + OTHER_PERSON).to_int() # Used to check if OTHER_PERSON is transferring the correct token token_crypto = get(TOKEN_PREFIX + OTHER_PERSON) if (from_address == address and funded_crypto == 0 and amount == amount_crypto and calling_script_hash == token_crypto): put(FUNDED_PREFIX + OTHER_PERSON, amount) return abort()
def onNEP17Payment(from_address: UInt160, amount: int, data: Any): """ If this smart contract receives NEO, it will mint an amount of wrapped NEO :param from_address: the address of the one who is trying to send cryptocurrency to this smart contract :type from_address: UInt160 :param amount: the amount of cryptocurrency that is being sent to the this smart contract :type amount: int :param data: any pertinent data that might validate the transaction :type data: Any """ # Use calling_script_hash to identify if the incoming token is NEO if calling_script_hash == NEO: mint(from_address, amount) else: abort()
def withdraw(stream_id: int, amount: int) -> bool: """ Withdraw funds from contract to recipient. Can be triggered by either recipient or sender Args: stream_id (int): Stream ID amount (int): Amount to withdraw Returns: bool: Success or failure """ stream = loadStream(stream_id) recipient = base64_decode(cast(str, stream['recipient'])) sender = base64_decode(cast(str, stream['sender'])) requester = "" if check_witness(recipient): print("Recipient requesting withdrawal") requester = cast(str, stream['recipient']) elif check_witness(sender): # TODO: should sender be able to request an advance to recipient? print("Sender requesting withdrawal to recipient") requester = cast(str, stream['sender']) else: print("Must be sender or recipient to withdraw") abort() remaining = cast(int, stream['remaining']) available = getAmountAvailableForWithdrawal(stream) assert amount > 0, 'nothing to do' assert available >= amount, 'withdrawal amount exceeds available funds' stream['remaining'] = remaining - amount call_contract(GAS, 'transfer', [executing_script_hash, recipient, amount, None]) if cast(int, stream['remaining']) == 0: deleteStream(stream) on_complete(stream_id) else: put(b'streams/' + stream_id.to_bytes(), json_serialize(stream)) on_withdraw(stream_id, requester, amount) return True
def onNEP17Payment(t_from: UInt160, t_amount: int, data: List[Any]): """ Triggered by a payment to the contract, which creates a new stream Args: t_from (UInt160): Sender of GAS t_amount (int): Amount of GAS sent data (List[Any]): Parameters for operations """ if calling_script_hash == GAS: assert len(t_from) == 20, 'invalid address' assert t_amount > 0, 'no funds transferred' p_len = len(data) assert p_len > 1, 'incorrect data length' p_operation = cast(str, data[0]) if p_operation == 'createStream': assert p_len == 4, 'incorrect arguments to createStream' recipient = cast(bytes, data[1]) start_time = cast(int, data[2]) stop_time = cast(int, data[3]) current_time = get_time assert len(recipient) == 20, 'invalid recipient scripthash' # assert start_time >= current_time, 'start time cannot be in the past' assert stop_time > start_time, 'stop time must be greater than start time' stream = newStream() stream['start'] = start_time stream['stop'] = stop_time stream['deposit'] = t_amount stream['remaining'] = t_amount stream['sender'] = base64_encode(t_from) stream['recipient'] = base64_encode(recipient) saveStream(stream) return abort()
def cancelStream(stream_id: int) -> bool: """ Cancel stream and make final disbursal of funds from contract to recipient and remainder to sender. Can be triggered by either recipient or sender Args: stream_id (int): Stream ID Returns: bool: Success or failure """ stream = loadStream(stream_id) recipient = base64_decode(cast(str, stream['recipient'])) sender = base64_decode(cast(str, stream['sender'])) requester = '' if check_witness(recipient): requester = cast(str, stream['recipient']) elif check_witness(sender): requester = cast(str, stream['sender']) else: print("Must be sender or recipient to cancel stream") abort() available = getAmountAvailableForWithdrawal(stream) remaining = cast(int, stream['remaining']) - available if available > 0: call_contract(GAS, 'transfer', [executing_script_hash, recipient, available, None]) if remaining > 0: call_contract(GAS, 'transfer', [executing_script_hash, sender, remaining, None]) deleteStream(stream) on_cancel(stream_id, requester) return True
def onNEP17Payment(from_address: UInt160, amount: int, data: Any): if data != "Transfer from caller to Ruler" and data != "Transfer from Ruler to caller": # just a mechanism to prevent accidental wrong payment abort()
def main(check: bool) -> int: if check: abort() return 123
def onNEP17Payment(from_address: UInt160, amount: int, data: Any): # accept GAS only if calling_script_hash != GAS: abort()
def add_liquidity(amount_token_a_desired: int, amount_token_b_desired: int, amount_token_a_min: int, amount_token_b_min: int, user_address: UInt160) -> List[int]: """ Adds liquidity to the pool, minting AMM tokens in the process. :param amount_token_a_desired: the quantity of token_a that the user wants to send to the liquidity pool :type amount_token_a_desired: int :param amount_token_b_desired: the quantity of token_b that the user wants to send to the liquidity pool :type amount_token_b_desired: int :param amount_token_a_min: the minimum quantity of token_a that the user wants to send to the liquidity pool :type amount_token_a_min: int :param amount_token_b_min: the minimum quantity of token_b that the user wants to send to the liquidity pool :type amount_token_b_min: int :param user_address: the user's address :type user_address: UInt160 :return: at index 0 and 1, the amount of token_a and token_b tokens that were transferred, respectively, and at index 2 the liquidity created in the mint :rtype: list :raise AssertionError: raised if the best value of a token is less than the minimum amount desired, if the user didn't allow enough money, or if the one calling this function is not the user_address """ # this verification exists thanks to a limitation in the TestEngine, it's returning None when using calling_script_hash # using just `user_address = calling_script hash` should be enough if runtime.calling_script_hash is not None: # TODO: remove the verification when the TestEngine starts sending a calling script hash user_address = runtime.calling_script_hash else: assert runtime.check_witness(user_address) reserve_token_a = storage.get(SUPPLY_KEY + TOKEN_A).to_int() reserve_token_b = storage.get(SUPPLY_KEY + TOKEN_B).to_int() # If there is no liquidity pool, then the values that will be used to mint and create a pool are the desired ones if reserve_token_a == 0 and reserve_token_b == 0: amount_token_a = amount_token_a_desired amount_token_b = amount_token_b_desired # If there is already a liquidity pool, the best value of token_b or token_a will be calculated and then they will be # minted and added to the pool else: amount_token_b_best = quote(amount_token_a_desired, reserve_token_a, reserve_token_b) if amount_token_b_best <= amount_token_b_desired: assert amount_token_b_best >= amount_token_b_min amount_token_a = amount_token_a_desired amount_token_b = amount_token_b_best else: amount_token_a_best = quote(amount_token_b_desired, reserve_token_b, reserve_token_a) assert amount_token_a_best <= amount_token_a_desired assert amount_token_a_best >= amount_token_a_min amount_token_a = amount_token_a_best amount_token_b = amount_token_b_desired amount_allowed_token_a = call_contract( UInt160(storage.get(TOKEN_A)), 'allowance', [user_address, runtime.executing_script_hash]) amount_allowed_token_b = call_contract( UInt160(storage.get(TOKEN_B)), 'allowance', [user_address, runtime.executing_script_hash]) if isinstance(amount_allowed_token_a, int) and isinstance( amount_allowed_token_b, int): assert amount_allowed_token_a >= amount_token_a and amount_allowed_token_b >= amount_token_b else: abort() call_contract(UInt160(storage.get(TOKEN_A)), 'transferFrom', [ runtime.executing_script_hash, user_address, runtime.executing_script_hash, amount_token_a, None ]) call_contract(UInt160(storage.get(TOKEN_B)), 'transferFrom', [ runtime.executing_script_hash, user_address, runtime.executing_script_hash, amount_token_b, None ]) # mint() will return the AMM tokens that were minted liquidity = mint(user_address) return [amount_token_a, amount_token_b, liquidity]
def onNEP17Payment(from_address: UInt160, amount: int, data: Any): if not runtime.calling_script_hash == storage.get( TOKEN_A) and not runtime.calling_script_hash == storage.get( TOKEN_B): abort()