Beispiel #1
0
def post_transfer(from_address: Union[UInt160, None],
                  to_address: Union[UInt160, None], amount: int, data: Any):
    """
    Checks if the one receiving DWHEAT tokens is a smart contract and if it's one the onPayment method will be called

    :param from_address: the address of the sender
    :param to_address: the address of the receiver
    :param amount: the amount of cryptocurrency that is being sent
    :param data: any pertinent data that might validate the transaction
    """
    if not isinstance(to_address, None):
        contract = get_contract(to_address)
        if not isinstance(contract, None):
            call_contract(to_address, 'onNEP17Payment',
                          [from_address, amount, data])
Beispiel #2
0
def _sendAmtPostFeesOptionalAccrue(invoker: UInt160, _token: UInt160,
                                   _amount: int, _feeRate: int,
                                   _accrue: bool) -> int:
    """
    Transfer collateral or paired tokens to invoker.
    Fees are not considered in _amount, but is always wiped in this method.
    If _accrue == True, accrue fees on the transferred tokens.
    Make sure you do not accrue fees if the fees have been accrued before. (Usually for paired tokens)
    And make sure you accrue fees if the fees have not been accrued before. (Usually for collateral)
    :param invoker: The wallet to receive the transferred tokens
    :param _token: the address of transferred token
    :param _amount: the amount of transferred token. Fees will be wiped out from _amount
    :param _feeRate: A fee is charged for all the tokens given out from Ruler. DECIMAL_BASE applied
    :param _accrue: Whether fees are accrued. If fees have already been accrued before, set this to False
    :return: the actual amount of token paid to invoker
    """
    fees = _amount * _feeRate // DECIMAL_BASE
    amount_to_pay = _amount - fees
    assert call_contract(_token, "transfer", [
        executing_script_hash, invoker, amount_to_pay,
        "collect paired token from ruler with rcTokens"
    ])
    if _accrue:
        original_fee = feesMap.get(_token).to_int()
        feesMap.put(_token, original_fee + fees)
    return amount_to_pay
Beispiel #3
0
def mint(account: UInt160, meta: bytes, lockedContent: bytes, royalties: bytes,
         data: Any) -> bytes:
    """
    Mint new token.

    :param account: the address of the account that is minting token
    :type account: UInt160
    :param meta: the metadata to use for this token
    :type meta: bytes
    :param lockedContent: the lock content to use for this token
    :type lockedContent: bytes
    :param royalties: the royalties to use for this token
    :type royalties: bytes
    :param data: whatever data is pertinent to the mint method
    :type data: Any
    :return: tokenId of the token minted
    :raise AssertionError: raised if mint fee is less than than 0 or if the account does not have enough to pay for it or if the contract is paused or if check witness fails.
    """
    assert not isPaused(), "GhostMarket contract is currently paused"

    fee = get_mint_fee()
    assert fee > 0, "Mint fee can't be < 0"
    assert check_witness(account), "Invalid witness"

    if fee > 0:
        # TODO use calling_script_hash instead of account
        success: bool = call_contract(
            GAS, 'transfer', [account, executing_script_hash, fee, None])
        assert success, "Fee payment failed!"

    return internal_mint(account, meta, lockedContent, royalties, data)
Beispiel #4
0
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
Beispiel #5
0
def calling_mint(address: UInt160, from_address: UInt160, data: Any) -> Any:
    """
    Transfer NEO to an account

    :return: whether the transfer was successful.
    :rtype: bool
    """
    return call_contract(address, 'mint', [from_address, data])
Beispiel #6
0
def post_transfer(from_address: Union[UInt160, None], to_address: Union[UInt160, None], amount: int, data: Any):
    """
    Checks if the one receiving NEP17 tokens is a smart contract and if it's one the onPayment method will be called

    :param from_address: the address of the sender
    :type from_address: UInt160
    :param to_address: the address of the receiver
    :type to_address: UInt160
    :param amount: the amount of cryptocurrency that is being sent
    :type amount: int
    :param data: any pertinent data that might validate the transaction
    :type data: Any
    """
    if not isinstance(to_address, None):    # TODO: change to 'is not None' when `is` semantic is implemented
        contract = get_contract(to_address)
        if not isinstance(contract, None):      # TODO: change to 'is not None' when `is` semantic is implemented
            call_contract(to_address, 'onPayment', [from_address, amount, data])
Beispiel #7
0
def claim_bounty(owner: UInt160, index: str, material: str) -> bool:
    unclaimed = get_unclaimed(index, material)

    # dynamic invoke to `mint` on relevant contract
    res: bool = call_contract(MATERIAL_WOOD, "mint", (owner, unclaimed))
    if res == True:
        put(CLAIM_TIMESTAMP + index.to_bytes() + material.to_bytes(), get_time)

    return True
Beispiel #8
0
def getFeeBalance() -> Any:
    """
    Get mint fees balance.

    :return: balance of mint fees.
    """
    balance = call_contract(GAS, 'balanceOf', [executing_script_hash])
    debug(['getFeeBalance: ', balance])
    return balance
Beispiel #9
0
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]
Beispiel #10
0
def _getColAmtFromRTokenAmt(_rTokenAmt: int, _col: UInt160, _rToken: UInt160,
                            _mintRatio: int) -> int:
    """
    Compute the amount of collateral of a pair, given the amount of rTokens
    :param _rTokenAmt: How many rTokens are given
    :param _col: pair attribute: address of the collateral token
    :param _rToken: pair attribute: address of the rToken
    :param _mintRatio: pair attribute: mint ratio: 1 collateral token for how many rToken. DECIMAL_BASE applied.
    :return: the amount of collateral
    """
    r_token_decimals = call_contract(_rToken, "decimals")
    collateral_token_decimals = call_contract(_col, "decimals")
    delta_decimals = cast(int, collateral_token_decimals) - cast(
        int, r_token_decimals)
    if delta_decimals >= 0:
        return _rTokenAmt * (10**delta_decimals) * DECIMAL_BASE // _mintRatio
    else:
        delta_decimals = -delta_decimals
        return _rTokenAmt * DECIMAL_BASE // (_mintRatio * 10**delta_decimals)
Beispiel #11
0
def post_transfer(from_address: Union[UInt160, None],
                  to_address: Union[UInt160, None], amount: int, data: Any):
    """
    Checks if the one receiving NEP17 tokens is a smart contract and if it's one the onPayment method will be called

    :param from_address: the address of the sender
    :type from_address: UInt160
    :param to_address: the address of the receiver
    :type to_address: UInt160
    :param amount: the amount of cryptocurrency that is being sent
    :type amount: int
    :param data: any pertinent data that might validate the transaction
    :type data: Any
    """
    if to_address is not None:
        contract = ContractManagement.get_contract(to_address)
        if contract is not None:
            call_contract(to_address, 'onNEP17Payment',
                          [from_address, amount, data])
Beispiel #12
0
def transfer_gas(from_address: UInt160, to_address: UInt160, amount: UInt160,
                 data: Any) -> Any:
    """
    Transfer GAS to an account

    :return: whether the transfer was successful.
    :rtype: bool
    """
    return call_contract(GAS, 'transfer',
                         [from_address, to_address, amount, data])
Beispiel #13
0
def _getRTokenAmtFromColAmt(_colAmt: int, _col: UInt160, _paired: UInt160,
                            _mintRatio: int) -> int:
    """
    Compute the amount of rTokens of a pair, given the amount of collateral
    :param _colAmt: How many collateral tokens are given
    :param _col: pair attribute: address of the collateral token
    :param _paired: pair attribute: address of the paired token
    :param _mintRatio: pair attribute: mint ratio: 1 collateral token for how many rToken. DECIMAL_BASE applied.
    :return: the amount of rTokens
    """
    # potential optimization: save the number of decimals as an attribute of pair?
    parity_token_decimals = call_contract(_paired, "decimals")
    collateral_token_decimals = call_contract(_col, "decimals")
    delta_decimals = cast(int, parity_token_decimals) - cast(
        int, collateral_token_decimals)
    if delta_decimals >= 0:
        return _colAmt * _mintRatio * (10**delta_decimals) // DECIMAL_BASE
    else:
        delta_decimals = -delta_decimals
        return _colAmt * _mintRatio // (DECIMAL_BASE * 10**delta_decimals)
Beispiel #14
0
def withdrawFee(account: UInt160) -> bool:
    """
    Withdraw mint fees.

    :param account: the address of the account that is withdrawing fees
    :type account: UInt160
    :return: whether the transaction was successful.
    :emits MintFeeWithdrawn: on success emits MintFeeWithdrawn
    :raise AssertionError: raised if witness is not verified.
    """
    assert verify(), '`acccount` is not allowed for withdrawFee'
    current_balance = cast(
        int, call_contract(GAS, 'balanceOf', [executing_script_hash]))
    on_withdraw_mint_fee(account, current_balance)
    debug(['withdrawFee: ', current_balance])

    status: bool = call_contract(
        GAS, 'transfer',
        [executing_script_hash, account, current_balance, None])
    return status
Beispiel #15
0
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
Beispiel #16
0
def repay(invoker: UInt160, _col: UInt160, _paired: UInt160, _expiry: int,
          _mintRatio: int, _rrTokenAmt: int) -> int:
    """
    Borrower repays the contract with rrTokens and paired tokens before expiry, and borrower receives collateral.
    NO fees charged on collateral
    :param invoker: The wallet that pays rrTokens and paired tokens to get collateral.
    :param _col: pair attribute: address of the collateral token
    :param _paired: pair attribute: address of the paired token
    :param _expiry: pair attribute: expiry timestamp in milliseconds
    :param _mintRatio: pair attribute: mint ratio: 1 collateral token for how many rToken. DECIMAL_BASE applied.
    :param _rrTokenAmt: How many rTokens and paired tokens will be paid to get collateral
    :return: the amount of collateral paid back
    """
    pair = _get_pair_with_assertion(_col, _paired, _expiry, _mintRatio)
    assert get_pair_attribute(pair,
                              "expiry").to_int() > time, "Ruler: pair expired"

    assert call_contract(_paired, "transfer", [
        invoker, executing_script_hash, _rrTokenAmt,
        "Transfer from caller to Ruler"
    ])
    rrToken_address = cast(UInt160, get_pair_attribute(pair, "rrToken"))
    call_contract(rrToken_address, "burnByRuler", [invoker, _rrTokenAmt])

    feesMap.put(
        _paired,
        feesMap.get(_paired).to_int() + _rrTokenAmt *
        get_pair_attribute(pair, 'feeRate').to_int() // DECIMAL_BASE)

    rcToken_address = cast(UInt160, get_pair_attribute(pair, "rcToken"))
    colAmountToPay = _getColAmtFromRTokenAmt(
        _rrTokenAmt, _col, rcToken_address,
        get_pair_attribute(pair, "mintRatio").to_int())
    assert call_contract(_col, "transfer", [
        executing_script_hash, invoker, colAmountToPay,
        "Transfer from Ruler to caller"
    ])

    return colAmountToPay
def post_transfer(from_address: Union[UInt160,
                                      None], to_address: Union[UInt160, None],
                  token_id: ByteString, data: Any):
    """
    Checks if the one receiving NEP11 tokens is a smart contract and if it's one the onPayment method will be called.

    :param from_address: the address of the sender
    :type from_address: UInt160
    :param to_address: the address of the receiver
    :type to_address: UInt160
    :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
    """
    # the transfer event will be fired
    on_transfer(from_address, to_address, 1, token_id)

    if not isinstance(to_address, None):
        contract = ContractManagement.get_contract(to_address)
        if not isinstance(contract, None):
            call_contract(to_address, 'onNEP11Payment',
                          [from_address, 1, token_id, data])
Beispiel #18
0
def mmDeposit(invoker: UInt160, _col: UInt160, _paired: UInt160, _expiry: int,
              _mintRatio: int, _rcTokenAmt: int) -> bool:
    """
    Special API: market make deposit
    Deposit paired token into the contract to receive a same amount of rcTokens immediately.
    I do not know the purpose of using this method. Trying to receive collateral instead of paired token?
    :param invoker: The wallet address that will pay paired tokens to get rcTokens
    :param _col: pair attribute: address of the collateral token
    :param _paired: pair attribute: address of the paired token
    :param _expiry: pair attribute: expiry timestamp in milliseconds
    :param _mintRatio: pair attribute: mint ratio: 1 collateral token for how many rTokens. DECIMAL_BASE applied.
    :param _rcTokenAmt: how many paired tokens to be deposited for the same amount of rcTokens
    :return: True (since the amount of minted rcToken always equals the amount of paired token paid)
    """
    pair_index = _get_pair_with_assertion(_col, _paired, _expiry, _mintRatio)
    _validateDepositInputs(pair_index)
    assert call_contract(_paired, "transfer", [
        invoker, executing_script_hash, _rcTokenAmt,
        "Transfer from caller to Ruler"
    ]), "Failed to transfer paired token from caller to Ruler"

    rcToken_address = cast(UInt160, get_pair_attribute(pair_index, 'rcToken'))
    call_contract(rcToken_address, "mint", [invoker, _rcTokenAmt])

    feeRate = get_pair_attribute(pair_index, 'feeRate').to_int()
    feesMap.put(
        _paired,
        feesMap.get(_paired).to_int() + _rcTokenAmt * feeRate // DECIMAL_BASE)

    colAmount = _getColAmtFromRTokenAmt(_rcTokenAmt, _col, rcToken_address,
                                        _mintRatio)
    colTotal_key = gen_pair_key(pair_index, 'colTotal')
    colTotal = pair_map.get(colTotal_key).to_int()
    pair_map.put(colTotal_key, colTotal + colAmount)

    return True
Beispiel #19
0
def collectFee(token: UInt160) -> int:
    """
    Withdraw the fee of a single token.
    No need to check witness? Because the fee is always given to the fee receiver.
    :param token: the fee in this token is withdrawn
    :return: the amount of fee withdrawn
    """
    # no need to check witness
    fee_receiver = get(FEE_RECEIVER_KEY)
    amount = feesMap.get(token).to_int()
    if amount > 0:
        feesMap.put(token, 0)
        assert call_contract(token, 'transfer', [
            executing_script_hash, fee_receiver, amount,
            bytearray(b'collect ') + token
        ])
    return amount
Beispiel #20
0
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()
Beispiel #21
0
def collectFees() -> bool:
    """
    Collect all the fees
    No need to check witness? Because the fee is always given to the fee receiver.
    :return: True
    """
    iterator = find(b'feesMap')
    fee_receiver = get(FEE_RECEIVER_KEY)
    while iterator.next():
        token_bytes = cast(bytes, iterator.value[0])
        token_bytes = token_bytes[
            7:]  # cut 'feesMap' at the beginning of the bytes
        token = cast(UInt160, token_bytes)
        fee_amount = cast(bytes, iterator.value[1]).to_int()
        if fee_amount > 0:
            feesMap.put(token, 0)
            assert call_contract(token, 'transfer', [
                executing_script_hash, fee_receiver, fee_amount, 'Collect Fees'
            ])
    return True
Beispiel #22
0
def main(scripthash: UInt160, method: str, args: list) -> Any:
    return contract.call_contract(scripthash, method, args)
def calling_approve(address: UInt160, spender: UInt160, amount: int) -> Any:
    return call_contract(address, 'approve', [spender, amount])
Beispiel #24
0
 def transfer(cls, from_address: UInt160, to_address: UInt160, amount: int,
              data: Any) -> bool:
     return cast(
         bool,
         call_contract(cls._hash, 'transfer',
                       [from_address, to_address, amount, data]))
Beispiel #25
0
 def balance_of(cls, account: UInt160) -> int:
     return cast(int, call_contract(cls._hash, 'balanceOf', [account]))
Beispiel #26
0
 def total_supply(cls) -> int:
     return cast(int, call_contract(cls._hash, 'totalSupply'))
Beispiel #27
0
 def decimals(cls) -> int:
     return cast(int, call_contract(cls._hash, 'decimals'))
Beispiel #28
0
 def symbol(cls) -> str:
     return cast(str, call_contract(cls._hash, 'symbol'))
Beispiel #29
0
def Main(scripthash: bytes, method: str, args: list):
    call_contract(scripthash, method, args)
def Main(scripthash: bytes):
    call_contract(scripthash)