From 14bda42e1f00ec207e50e857cbab7fc015ee2135 Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Tue, 27 Aug 2019 11:47:35 -0300 Subject: [PATCH 1/3] Handle `null` returns from `ethereum.call` --- index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/index.ts b/index.ts index fe52c15..dc0f456 100644 --- a/index.ts +++ b/index.ts @@ -12,7 +12,7 @@ export declare namespace store { /** Host Ethereum interface */ declare namespace ethereum { - function call(call: SmartContractCall): Array + function call(call: SmartContractCall): Array | null } /** Host IPFS interface */ @@ -1381,7 +1381,10 @@ export class SmartContract { call(name: string, params: Array): Array { let call = new SmartContractCall(this._name, this._address, name, params) - return ethereum.call(call) + let result = ethereum.call(call) + assert(result != null, 'Call reverted, consider using `try_' + name + + '` to handle this in the mapping.') + return ethereum.call(call) as Array } } From e2ae2a2f1a1076f9d31762fd60275a2db105afb2 Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Tue, 27 Aug 2019 19:01:31 -0300 Subject: [PATCH 2/3] Add `tryCall` and `CallResult` for fallible contract calls --- index.ts | 67 +++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/index.ts b/index.ts index dc0f456..720b75f 100644 --- a/index.ts +++ b/index.ts @@ -656,7 +656,7 @@ export class BigDecimal { let aTotalLength = BigInt.fromI32(aRelevantDigits as i32) + a.exp let bTotalLength = BigInt.fromI32(bRelevantDigits as i32) + b.exp - + // If a and b are positive then the longer one is larger. // Otherwise the shorter one is larger. if (aTotalLength > bTotalLength) { @@ -664,7 +664,7 @@ export class BigDecimal { } else if (bTotalLength > aTotalLength) { return aIsNeg ? 1 : -1 } - + // We now know that a and b have the same sign and total length. If a and b // are both negative then the one of lesser magnitude is the largest, // however since in two's complement the magnitude is flipped, we may use @@ -681,7 +681,7 @@ export class BigDecimal { return 1 } } - + return 0 } } @@ -768,8 +768,9 @@ export class EthereumValue { } toTupleArray(): Array { - assert(this.kind == EthereumValueKind.ARRAY || this.kind == EthereumValueKind.FIXED_ARRAY, - 'EthereumValue is not an array.' + assert( + this.kind == EthereumValueKind.ARRAY || this.kind == EthereumValueKind.FIXED_ARRAY, + 'EthereumValue is not an array.', ) let valueArray = this.toArray() let out = new Array(valueArray.length) @@ -931,7 +932,7 @@ export class EthereumValue { static fromTupleArray(values: Array): EthereumValue { let out = new Array(values.length) - for(let i: i32 = 0; i < values.length; i++) { + for (let i: i32 = 0; i < values.length; i++) { out[i] = EthereumValue.fromTuple(values[i]) } return EthereumValue.fromArray(out) @@ -1382,10 +1383,22 @@ export class SmartContract { call(name: string, params: Array): Array { let call = new SmartContractCall(this._name, this._address, name, params) let result = ethereum.call(call) - assert(result != null, 'Call reverted, consider using `try_' + name + - '` to handle this in the mapping.') + assert( + result != null, + 'Call reverted, consider using `try_' + name + '` to handle this in the mapping.', + ) return ethereum.call(call) as Array } + + tryCall(name: string, params: Array): CallResult> { + let call = new SmartContractCall(this._name, this._address, name, params) + let result = ethereum.call(call) + if (result == null) { + return new CallResult() + } else { + return CallResult.fromValue(result as Array) + } + } } /** Type hint for JSON values. */ @@ -1472,3 +1485,41 @@ export class DataSourceTemplate { dataSource.create(name, params) } } + +// This is used to wrap a generic so that it can be unioned with `null`, working around limitations +// with primitives. +class Wrapped { + inner: T + + constructor(inner: T) { + this.inner = inner + } +} + +export class CallResult { + // `null` indicates a reverted call. + private _value: Wrapped | null + + constructor() { + this._value = null + } + + static fromValue(value: T): CallResult { + let result = new CallResult() + result._value = new Wrapped(value) + return result + } + + get reverted(): bool { + return this._value == null + } + + get value(): T { + assert( + !this.reverted, + 'accessed value of a reverted call, ' + + 'please check the `reverted` field before accessing the `value` field', + ) + return (this._value as Wrapped).inner + } +} From 9cccc7ef29c4c9fc3f876e9bd0d5cc0a0cb592b8 Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Mon, 2 Sep 2019 12:22:13 -0300 Subject: [PATCH 3/3] index: Make error message more helpful --- index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.ts b/index.ts index 720b75f..e7656f9 100644 --- a/index.ts +++ b/index.ts @@ -1385,7 +1385,8 @@ export class SmartContract { let result = ethereum.call(call) assert( result != null, - 'Call reverted, consider using `try_' + name + '` to handle this in the mapping.', + 'Call reverted, probably because an `assert` or `require` in the contract failed, ' + + 'consider using `try_' + name + '` to handle this in the mapping.', ) return ethereum.call(call) as Array }