Difference between revisions of "Super.init() caller"

From RunWiki
Jump to: navigation, search
(Created page with "<div><br></div><div><div><div>Redefining init() with a call to super.init() inside causes issues:</div><div>      - "minting = false, sending = true" (should be...")
 
 
(9 intermediate revisions by the same user not shown)
Line 1: Line 1:
<div><br></div><div><div><div>Redefining init() with a call to super.init() inside causes issues:</div><div>&nbsp; &nbsp; &nbsp; - "minting = false, sending = true" (should be the opposite)</div><div>&nbsp; &nbsp; &nbsp; - 'caller' variable which should be the class TokenA,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;but becomes instead: Object &lt;Object &lt;Object &lt;Object &lt;Complex prototype&gt;&gt;&gt;&gt; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; location: 'error://Undeployed',</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; origin: 'error://Undeployed',</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nonce: 0,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; owner: 'mo61dRQheTcBMf1d7UEv91ybaDqXUyKUwd',</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; satoshis: undefined</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; Note that despite these issues, it works nontheless, since it detects as 'sending' instead of 'minting',</div><div>&nbsp; &nbsp; &nbsp; which manages to still pass the check and mint the token.</div></div></div><div></div><div>Full Code you can test (just comment the init() entirely in TokenA) to see the difference in logs while running it:</div><div></div><div></div><div></div><div>[[Category:Issues]]</div>
+
When you extend Token, redefining init() with a call to super.init() inside causes issues:
 +
 
 +
- "minting = false, sending = true" (should be the opposite)
 +
 
 +
- 'caller' variable which should be the class TokenA, but becomes instead:
 +
 
 +
<syntaxhighlight lang="javascript">Object <Object <Object <Object <Complex prototype>>>> {
 +
                location: 'error://Undeployed',
 +
                origin: 'error://Undeployed',
 +
                nonce: 0,
 +
                owner: 'mo61dRQheTcBMf1d7UEv91ybaDqXUyKUwd',
 +
                satoshis: undefined
 +
            }</syntaxhighlight>
 +
 +
 
 +
Note that despite these issues, it works nonetheless, since it detects the situation as 'sending' instead of 'minting', which manages to still pass the check and mint the token.
 +
 
 +
 
 +
 
 +
Full Code you can test, just comment the init() entirely in TokenA to see the difference in logs while running it:
 +
 
 +
 
 +
<syntaxhighlight lang="javascript">
 +
const Run = require('../../libs/run.0.6.44.node.min.js');
 +
const log = console.log
 +
 
 +
let run = new Run({ network: 'mock', trust: 'state' })
 +
 
 +
// pure copy of the Token20 with only some added logging
 +
class Token20Copy extends Jig {
 +
    init(amount, owner) {
 +
        this._checkAmount(amount)
 +
 
 +
        // The base Token class cannot be created on its own
 +
        const extended = this.constructor !== Token20Copy
 +
        if (!extended) throw new Error('Token must be extended')
 +
 
 +
        // Make sure we are calling from ourself
 +
        const minting = caller === this.constructor
 +
        const sending = caller && caller.constructor === this.constructor
 +
        console.log("minting =", minting, ", sending =", sending, ", \n caller =", caller)
 +
        if (!minting && !sending) throw new Error('Must create token using mint()')
 +
 
 +
        this.sender = sending ? caller.owner : null
 +
        this.amount = amount
 +
        if (owner) this.owner = owner
 +
    }
 +
 
 +
    static mint(amount, owner) {
 +
        this.supply += amount
 +
        return new this(amount, owner)
 +
    }
 +
 
 +
    send(to, amount = this.amount) {
 +
        this._checkAmount(amount)
 +
 
 +
        if (this.amount === amount) {
 +
            this.destroy()
 +
        } else if (this.amount > amount) {
 +
            this.amount -= amount
 +
        } else {
 +
            throw new Error('Not enough funds')
 +
        }
 +
 
 +
        return new this.constructor(amount, to)
 +
    }
 +
 
 +
    combine(...tokens) {
 +
        // If no tokens to combine, nothing to do
 +
        if (!tokens.length) return this
 +
 
 +
        // Each token to combine must all be of this type
 +
        const all = tokens.concat(this)
 +
        if (all.some(token => token.constructor !== this.constructor)) {
 +
            throw new Error('Cannot combine different token classes')
 +
        }
 +
 
 +
        // Check for duplicate tokens in the array
 +
        const countOf = token => all.reduce((count, next) => next === token ? count + 1 : count, 0)
 +
        if (all.some(token => countOf(token) > 1)) throw new Error('Cannot combine duplicate tokens')
 +
 
 +
        // Destroy each token, absorbing it into this one
 +
        tokens.forEach(token => {
 +
            this.amount += token.amount
 +
            token.destroy()
 +
        })
 +
 
 +
        // There is no sender for combined tokens
 +
        this.sender = null
 +
 
 +
        // Make sure our new amount is within safe range
 +
        this._checkAmount(this.amount)
 +
 
 +
        return this
 +
    }
 +
 
 +
    destroy() {
 +
        super.destroy()
 +
 
 +
        this.amount = 0
 +
        this.sender = null
 +
    }
 +
 
 +
    _checkAmount(amount) {
 +
        if (typeof amount !== 'number') throw new Error('amount is not a number')
 +
        if (!Number.isInteger(amount)) throw new Error('amount must be an integer')
 +
        if (amount <= 0) throw new Error('amount must be positive')
 +
        if (amount > Number.MAX_SAFE_INTEGER) throw new Error('amount too large')
 +
    }
 +
}
 +
 
 +
 
 +
class TokenA extends Token20Copy {
 +
    /** Redefining init() with a call to super.init() inside causes issues:
 +
    * - "minting = false, sending = true" (should be the opposite)
 +
    * - 'caller' variable which should be the class TokenA,
 +
    *    but becomes instead: Object <Object <Object <Object <Complex prototype>>>> {
 +
    *          location: 'error://Undeployed',
 +
    *          origin: 'error://Undeployed',
 +
    *          nonce: 0,
 +
    *          owner: 'mo61dRQheTcBMf1d7UEv91ybaDqXUyKUwd',
 +
    *          satoshis: undefined
 +
    *      }
 +
    *
 +
    * Note that despite these issues, it works nontheless, since it detects as 'sending' instead of 'minting',
 +
    * which manages to still pass the check and mint the token.
 +
    **/
 +
    init(amount, owner) {
 +
        super.init(amount, owner)
 +
    }
 +
}
 +
TokenA.decimals = 2
 +
TokenA.ticker = 'TokenA'
 +
TokenA.currency = 'TokenA'
 +
 
 +
 
 +
async function displayTokensOnAddress() {
 +
    await run.inventory.sync()
 +
    const tokens = run.inventory.jigs
 +
    log("---tokens on address:", tokens.length)
 +
    for (const token of tokens) {
 +
        log("token:", token.constructor.ticker, token.amount / (10 ** token.constructor.decimals), token.constructor.origin)
 +
    }
 +
    log("---end---\n")
 +
}
 +
 
 +
 
 +
async function _main() {
 +
    const owner = run.owner.address
 +
    await displayTokensOnAddress()
 +
 
 +
    TokenA = run.deploy(TokenA)
 +
    await run.sync()
 +
    TokenA = await run.load(TokenA.origin)
 +
    log("TokenA.owner =", TokenA.owner)
 +
 
 +
    log("minting TokenA")
 +
    let tokenACoins = TokenA.mint(10 * 10 ** TokenA.decimals, owner)
 +
    await run.sync()
 +
 
 +
    await displayTokensOnAddress()
 +
}
 +
 
 +
_main()
 +
 
 +
</syntaxhighlight>
 +
 
 +
[[Category:Issues]]

Latest revision as of 08:13, 1 February 2026

When you extend Token, redefining init() with a call to super.init() inside causes issues:

- "minting = false, sending = true" (should be the opposite)

- 'caller' variable which should be the class TokenA, but becomes instead:

Object <Object <Object <Object <Complex prototype>>>> {
                location: 'error://Undeployed',
                origin: 'error://Undeployed',
                nonce: 0,
                owner: 'mo61dRQheTcBMf1d7UEv91ybaDqXUyKUwd',
                satoshis: undefined
            }


Note that despite these issues, it works nonetheless, since it detects the situation as 'sending' instead of 'minting', which manages to still pass the check and mint the token.


Full Code you can test, just comment the init() entirely in TokenA to see the difference in logs while running it:


const Run = require('../../libs/run.0.6.44.node.min.js');
const log = console.log

let run = new Run({ network: 'mock', trust: 'state' })

// pure copy of the Token20 with only some added logging
class Token20Copy extends Jig {
    init(amount, owner) {
        this._checkAmount(amount)

        // The base Token class cannot be created on its own
        const extended = this.constructor !== Token20Copy
        if (!extended) throw new Error('Token must be extended')

        // Make sure we are calling from ourself
        const minting = caller === this.constructor
        const sending = caller && caller.constructor === this.constructor
        console.log("minting =", minting, ", sending =", sending, ", \n caller =", caller)
        if (!minting && !sending) throw new Error('Must create token using mint()')

        this.sender = sending ? caller.owner : null
        this.amount = amount
        if (owner) this.owner = owner
    }

    static mint(amount, owner) {
        this.supply += amount
        return new this(amount, owner)
    }

    send(to, amount = this.amount) {
        this._checkAmount(amount)

        if (this.amount === amount) {
            this.destroy()
        } else if (this.amount > amount) {
            this.amount -= amount
        } else {
            throw new Error('Not enough funds')
        }

        return new this.constructor(amount, to)
    }

    combine(...tokens) {
        // If no tokens to combine, nothing to do
        if (!tokens.length) return this

        // Each token to combine must all be of this type
        const all = tokens.concat(this)
        if (all.some(token => token.constructor !== this.constructor)) {
            throw new Error('Cannot combine different token classes')
        }

        // Check for duplicate tokens in the array
        const countOf = token => all.reduce((count, next) => next === token ? count + 1 : count, 0)
        if (all.some(token => countOf(token) > 1)) throw new Error('Cannot combine duplicate tokens')

        // Destroy each token, absorbing it into this one
        tokens.forEach(token => {
            this.amount += token.amount
            token.destroy()
        })

        // There is no sender for combined tokens
        this.sender = null

        // Make sure our new amount is within safe range
        this._checkAmount(this.amount)

        return this
    }

    destroy() {
        super.destroy()

        this.amount = 0
        this.sender = null
    }

    _checkAmount(amount) {
        if (typeof amount !== 'number') throw new Error('amount is not a number')
        if (!Number.isInteger(amount)) throw new Error('amount must be an integer')
        if (amount <= 0) throw new Error('amount must be positive')
        if (amount > Number.MAX_SAFE_INTEGER) throw new Error('amount too large')
    }
}


class TokenA extends Token20Copy {
    /** Redefining init() with a call to super.init() inside causes issues:
     * - "minting = false, sending = true" (should be the opposite)
     * - 'caller' variable which should be the class TokenA,
     *    but becomes instead: Object <Object <Object <Object <Complex prototype>>>> {
     *           location: 'error://Undeployed',
     *           origin: 'error://Undeployed',
     *           nonce: 0,
     *           owner: 'mo61dRQheTcBMf1d7UEv91ybaDqXUyKUwd',
     *           satoshis: undefined
     *       }
     * 
     * Note that despite these issues, it works nontheless, since it detects as 'sending' instead of 'minting',
     * which manages to still pass the check and mint the token.
     **/
    init(amount, owner) {
        super.init(amount, owner)
    }
}
TokenA.decimals = 2
TokenA.ticker = 'TokenA'
TokenA.currency = 'TokenA'


async function displayTokensOnAddress() {
    await run.inventory.sync()
    const tokens = run.inventory.jigs
    log("---tokens on address:", tokens.length)
    for (const token of tokens) {
        log("token:", token.constructor.ticker, token.amount / (10 ** token.constructor.decimals), token.constructor.origin)
    }
    log("---end---\n")
}


async function _main() {
    const owner = run.owner.address
    await displayTokensOnAddress()

    TokenA = run.deploy(TokenA)
    await run.sync()
    TokenA = await run.load(TokenA.origin)
    log("TokenA.owner =", TokenA.owner)

    log("minting TokenA")
    let tokenACoins = TokenA.mint(10 * 10 ** TokenA.decimals, owner)
    await run.sync()

    await displayTokensOnAddress()
}

_main()