2022-09-05 16:57:11 -04:00
"use strict" ;
Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
exports . Reader = exports . Writer = exports . Coder = exports . checkResultErrors = exports . Result = exports . WordSize = void 0 ;
2022-09-15 22:58:45 -04:00
const index _js _1 = require ( "../../utils/index.js" ) ;
2023-01-26 23:36:26 -05:00
/ * *
* @ _ignore :
* /
2022-09-05 16:57:11 -04:00
exports . WordSize = 32 ;
const Padding = new Uint8Array ( exports . WordSize ) ;
// Properties used to immediate pass through to the underlying object
// - `then` is used to detect if an object is a Promise for await
const passProperties = [ "then" ] ;
const _guard = { } ;
2023-01-26 23:36:26 -05:00
function throwError ( name , error ) {
const wrapped = new Error ( ` deferred error during ABI decoding triggered accessing ${ name } ` ) ;
wrapped . error = error ;
throw wrapped ;
}
2022-11-30 15:44:23 -05:00
/ * *
* A [ [ Result ] ] is a sub - class of Array , which allows accessing any
* of its values either positionally by its index or , if keys are
* provided by its name .
*
* @ _docloc : api / abi
* /
2022-09-05 16:57:11 -04:00
class Result extends Array {
2023-01-26 23:36:26 -05:00
# names ;
2022-11-30 15:44:23 -05:00
/ * *
* @ private
* /
2023-01-26 23:36:26 -05:00
constructor ( ... args ) {
// To properly sub-class Array so the other built-in
// functions work, the constructor has to behave fairly
// well. So, in the event we are created via fromItems()
// we build the read-only Result object we want, but on
// any other input, we use the default constructor
// constructor(guard: any, items: Array<any>, keys?: Array<null | string>);
const guard = args [ 0 ] ;
let items = args [ 1 ] ;
let names = ( args [ 2 ] || [ ] ) . slice ( ) ;
let wrap = true ;
if ( guard !== _guard ) {
items = args ;
names = [ ] ;
wrap = false ;
2022-09-05 16:57:11 -04:00
}
2023-01-26 23:36:26 -05:00
// Can't just pass in ...items since an array of length 1
// is a special case in the super.
super ( items . length ) ;
items . forEach ( ( item , index ) => { this [ index ] = item ; } ) ;
// Find all unique keys
const nameCounts = names . reduce ( ( accum , name ) => {
if ( typeof ( name ) === "string" ) {
accum . set ( name , ( accum . get ( name ) || 0 ) + 1 ) ;
}
return accum ;
} , ( new Map ( ) ) ) ;
// Remove any key thats not unique
this . # names = Object . freeze ( items . map ( ( item , index ) => {
const name = names [ index ] ;
if ( name != null && nameCounts . get ( name ) === 1 ) {
return name ;
}
return null ;
} ) ) ;
if ( ! wrap ) {
return ;
}
// A wrapped Result is immutable
2022-09-05 16:57:11 -04:00
Object . freeze ( this ) ;
2023-01-26 23:36:26 -05:00
// Proxy indices and names so we can trap deferred errors
2022-09-05 16:57:11 -04:00
return new Proxy ( this , {
get : ( target , prop , receiver ) => {
if ( typeof ( prop ) === "string" ) {
2023-01-26 23:36:26 -05:00
// Index accessor
2022-09-05 16:57:11 -04:00
if ( prop . match ( /^[0-9]+$/ ) ) {
2022-09-15 22:58:45 -04:00
const index = ( 0 , index _js _1 . getNumber ) ( prop , "%index" ) ;
2022-09-05 16:57:11 -04:00
if ( index < 0 || index >= this . length ) {
throw new RangeError ( "out of result range" ) ;
}
const item = target [ index ] ;
if ( item instanceof Error ) {
2023-01-26 23:36:26 -05:00
throwError ( ` index ${ index } ` , item ) ;
2022-09-05 16:57:11 -04:00
}
return item ;
}
// Pass important checks (like `then` for Promise) through
2023-01-26 23:36:26 -05:00
if ( passProperties . indexOf ( prop ) >= 0 ) {
2022-09-05 16:57:11 -04:00
return Reflect . get ( target , prop , receiver ) ;
}
2023-01-26 23:36:26 -05:00
const value = target [ prop ] ;
if ( value instanceof Function ) {
// Make sure functions work with private variables
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#no_private_property_forwarding
return function ( ... args ) {
return value . apply ( ( this === receiver ) ? target : this , args ) ;
} ;
}
else if ( ! ( prop in target ) ) {
// Possible name accessor
return target . getValue . apply ( ( this === receiver ) ? target : this , [ prop ] ) ;
2022-09-05 16:57:11 -04:00
}
}
return Reflect . get ( target , prop , receiver ) ;
}
} ) ;
}
2023-01-26 23:36:26 -05:00
/ * *
* Returns the Result as a normal Array .
*
* This will throw if there are any outstanding deferred
* errors .
* /
toArray ( ) {
2023-02-18 22:18:42 -05:00
const result = [ ] ;
2023-01-26 23:36:26 -05:00
this . forEach ( ( item , index ) => {
if ( item instanceof Error ) {
throwError ( ` index ${ index } ` , item ) ;
2022-09-05 16:57:11 -04:00
}
2023-02-18 22:18:42 -05:00
result . push ( item ) ;
2023-01-26 23:36:26 -05:00
} ) ;
2023-02-18 22:18:42 -05:00
return result ;
2023-01-26 23:36:26 -05:00
}
/ * *
* Returns the Result as an Object with each name - value pair .
*
* This will throw if any value is unnamed , or if there are
* any outstanding deferred errors .
* /
toObject ( ) {
return this . # names . reduce ( ( accum , name , index ) => {
( 0 , index _js _1 . assert ) ( name != null , "value at index ${ index } unnamed" , "UNSUPPORTED_OPERATION" , {
operation : "toObject()"
} ) ;
// Add values for names that don't conflict
if ( ! ( name in accum ) ) {
accum [ name ] = this . getValue ( name ) ;
}
return accum ;
} , { } ) ;
2022-09-05 16:57:11 -04:00
}
2022-11-30 15:44:23 -05:00
/ * *
* @ _ignore
* /
2022-09-05 16:57:11 -04:00
slice ( start , end ) {
if ( start == null ) {
start = 0 ;
}
2023-02-18 22:18:42 -05:00
if ( start < 0 ) {
start += this . length ;
if ( start < 0 ) {
start = 0 ;
}
}
2022-09-05 16:57:11 -04:00
if ( end == null ) {
end = this . length ;
}
2023-02-18 22:18:42 -05:00
if ( end < 0 ) {
end += this . length ;
if ( end < 0 ) {
end = 0 ;
}
}
if ( end > this . length ) {
end = this . length ;
}
2023-01-26 23:36:26 -05:00
const result = [ ] , names = [ ] ;
2022-09-05 16:57:11 -04:00
for ( let i = start ; i < end ; i ++ ) {
2023-01-26 23:36:26 -05:00
result . push ( this [ i ] ) ;
names . push ( this . # names [ i ] ) ;
}
return new Result ( _guard , result , names ) ;
}
/ * *
* @ _ignore
* /
filter ( callback , thisArg ) {
const result = [ ] , names = [ ] ;
for ( let i = 0 ; i < this . length ; i ++ ) {
const item = this [ i ] ;
if ( item instanceof Error ) {
throwError ( ` index ${ i } ` , item ) ;
2022-09-05 16:57:11 -04:00
}
2023-01-26 23:36:26 -05:00
if ( callback . call ( thisArg , item , i , this ) ) {
result . push ( item ) ;
names . push ( this . # names [ i ] ) ;
2022-09-05 16:57:11 -04:00
}
}
2023-01-26 23:36:26 -05:00
return new Result ( _guard , result , names ) ;
2022-09-05 16:57:11 -04:00
}
2023-05-18 17:26:59 -04:00
/ * *
* @ _ignore
* /
map ( callback , thisArg ) {
const result = [ ] ;
for ( let i = 0 ; i < this . length ; i ++ ) {
const item = this [ i ] ;
if ( item instanceof Error ) {
throwError ( ` index ${ i } ` , item ) ;
}
result . push ( callback . call ( thisArg , item , i , this ) ) ;
}
return result ;
}
2022-11-30 15:44:23 -05:00
/ * *
* Returns the value for % % name % % .
*
* Since it is possible to have a key whose name conflicts with
* a method on a [ [ Result ] ] or its superclass Array , or any
* JavaScript keyword , this ensures all named values are still
* accessible by name .
* /
2022-09-05 16:57:11 -04:00
getValue ( name ) {
2023-01-26 23:36:26 -05:00
const index = this . # names . indexOf ( name ) ;
if ( index === - 1 ) {
return undefined ;
}
const value = this [ index ] ;
if ( value instanceof Error ) {
throwError ( ` property ${ JSON . stringify ( name ) } ` , value . error ) ;
2022-09-05 16:57:11 -04:00
}
2023-01-26 23:36:26 -05:00
return value ;
2022-09-05 16:57:11 -04:00
}
2022-11-30 15:44:23 -05:00
/ * *
* Creates a new [ [ Result ] ] for % % items % % with each entry
* also accessible by its corresponding name in % % keys % % .
* /
2022-09-05 16:57:11 -04:00
static fromItems ( items , keys ) {
return new Result ( _guard , items , keys ) ;
}
}
exports . Result = Result ;
2022-11-30 15:44:23 -05:00
/ * *
* Returns all errors found in a [ [ Result ] ] .
*
* Since certain errors encountered when creating a [ [ Result ] ] do
* not impact the ability to continue parsing data , they are
* deferred until they are actually accessed . Hence a faulty string
* in an Event that is never used does not impact the program flow .
*
* However , sometimes it may be useful to access , identify or
* validate correctness of a [ [ Result ] ] .
*
* @ _docloc api / abi
* /
2022-09-05 16:57:11 -04:00
function checkResultErrors ( result ) {
// Find the first error (if any)
const errors = [ ] ;
const checkErrors = function ( path , object ) {
if ( ! Array . isArray ( object ) ) {
return ;
}
for ( let key in object ) {
const childPath = path . slice ( ) ;
childPath . push ( key ) ;
try {
checkErrors ( childPath , object [ key ] ) ;
}
catch ( error ) {
errors . push ( { path : childPath , error : error } ) ;
}
}
} ;
checkErrors ( [ ] , result ) ;
return errors ;
}
exports . checkResultErrors = checkResultErrors ;
function getValue ( value ) {
2022-12-09 18:24:58 -05:00
let bytes = ( 0 , index _js _1 . toBeArray ) ( value ) ;
2022-11-09 02:57:02 -05:00
( 0 , index _js _1 . assert ) ( bytes . length <= exports . WordSize , "value out-of-bounds" , "BUFFER_OVERRUN" , { buffer : bytes , length : exports . WordSize , offset : bytes . length } ) ;
2022-09-05 16:57:11 -04:00
if ( bytes . length !== exports . WordSize ) {
2022-09-15 22:58:45 -04:00
bytes = ( 0 , index _js _1 . getBytesCopy ) ( ( 0 , index _js _1 . concat ) ( [ Padding . slice ( bytes . length % exports . WordSize ) , bytes ] ) ) ;
2022-09-05 16:57:11 -04:00
}
return bytes ;
}
2022-11-30 15:44:23 -05:00
/ * *
* @ _ignore
* /
2022-09-05 16:57:11 -04:00
class Coder {
// The coder name:
// - address, uint256, tuple, array, etc.
name ;
// The fully expanded type, including composite types:
// - address, uint256, tuple(address,bytes), uint256[3][4][], etc.
type ;
// The localName bound in the signature, in this example it is "baz":
// - tuple(address foo, uint bar) baz
localName ;
// Whether this type is dynamic:
// - Dynamic: bytes, string, address[], tuple(boolean[]), etc.
// - Not Dynamic: address, uint256, boolean[3], tuple(address, uint8)
dynamic ;
constructor ( name , type , localName , dynamic ) {
2022-09-15 22:58:45 -04:00
( 0 , index _js _1 . defineProperties ) ( this , { name , type , localName , dynamic } , {
2022-09-05 16:57:11 -04:00
name : "string" , type : "string" , localName : "string" , dynamic : "boolean"
} ) ;
}
_throwError ( message , value ) {
2022-11-09 02:57:02 -05:00
( 0 , index _js _1 . assertArgument ) ( false , message , this . localName , value ) ;
2022-09-05 16:57:11 -04:00
}
}
exports . Coder = Coder ;
2022-11-30 15:44:23 -05:00
/ * *
* @ _ignore
* /
2022-09-05 16:57:11 -04:00
class Writer {
// An array of WordSize lengthed objects to concatenation
# data ;
# dataLength ;
constructor ( ) {
this . # data = [ ] ;
this . # dataLength = 0 ;
}
get data ( ) {
2022-09-15 22:58:45 -04:00
return ( 0 , index _js _1 . concat ) ( this . # data ) ;
2022-09-05 16:57:11 -04:00
}
get length ( ) { return this . # dataLength ; }
# writeData ( data ) {
this . # data . push ( data ) ;
this . # dataLength += data . length ;
return data . length ;
}
appendWriter ( writer ) {
2022-09-15 22:58:45 -04:00
return this . # writeData ( ( 0 , index _js _1 . getBytesCopy ) ( writer . data ) ) ;
2022-09-05 16:57:11 -04:00
}
// Arrayish item; pad on the right to *nearest* WordSize
writeBytes ( value ) {
2022-09-15 22:58:45 -04:00
let bytes = ( 0 , index _js _1 . getBytesCopy ) ( value ) ;
2022-09-05 16:57:11 -04:00
const paddingOffset = bytes . length % exports . WordSize ;
if ( paddingOffset ) {
2022-09-15 22:58:45 -04:00
bytes = ( 0 , index _js _1 . getBytesCopy ) ( ( 0 , index _js _1 . concat ) ( [ bytes , Padding . slice ( paddingOffset ) ] ) ) ;
2022-09-05 16:57:11 -04:00
}
return this . # writeData ( bytes ) ;
}
// Numeric item; pad on the left *to* WordSize
writeValue ( value ) {
return this . # writeData ( getValue ( value ) ) ;
}
// Inserts a numeric place-holder, returning a callback that can
// be used to asjust the value later
writeUpdatableValue ( ) {
const offset = this . # data . length ;
this . # data . push ( Padding ) ;
this . # dataLength += exports . WordSize ;
return ( value ) => {
this . # data [ offset ] = getValue ( value ) ;
} ;
}
}
exports . Writer = Writer ;
2022-11-30 15:44:23 -05:00
/ * *
* @ _ignore
* /
2022-09-05 16:57:11 -04:00
class Reader {
// Allows incomplete unpadded data to be read; otherwise an error
// is raised if attempting to overrun the buffer. This is required
// to deal with an old Solidity bug, in which event data for
// external (not public thoguh) was tightly packed.
allowLoose ;
# data ;
# offset ;
2024-01-12 19:47:16 -05:00
# bytesRead ;
# parent ;
# maxInflation ;
constructor ( data , allowLoose , maxInflation ) {
2022-09-15 22:58:45 -04:00
( 0 , index _js _1 . defineProperties ) ( this , { allowLoose : ! ! allowLoose } ) ;
this . # data = ( 0 , index _js _1 . getBytesCopy ) ( data ) ;
2024-01-12 19:47:16 -05:00
this . # bytesRead = 0 ;
this . # parent = null ;
this . # maxInflation = ( maxInflation != null ) ? maxInflation : 1024 ;
2022-09-05 16:57:11 -04:00
this . # offset = 0 ;
}
2022-09-15 22:58:45 -04:00
get data ( ) { return ( 0 , index _js _1 . hexlify ) ( this . # data ) ; }
2022-09-05 16:57:11 -04:00
get dataLength ( ) { return this . # data . length ; }
get consumed ( ) { return this . # offset ; }
get bytes ( ) { return new Uint8Array ( this . # data ) ; }
2024-01-12 19:47:16 -05:00
# incrementBytesRead ( count ) {
if ( this . # parent ) {
return this . # parent . # incrementBytesRead ( count ) ;
}
this . # bytesRead += count ;
// Check for excessive inflation (see: #4537)
( 0 , index _js _1 . assert ) ( this . # maxInflation < 1 || this . # bytesRead <= this . # maxInflation * this . dataLength , ` compressed ABI data exceeds inflation ratio of ${ this . # maxInflation } ( see: https:/ \/ github.com/ethers-io/ethers.js/issues/4537 ) ` , "BUFFER_OVERRUN" , {
buffer : ( 0 , index _js _1 . getBytesCopy ) ( this . # data ) , offset : this . # offset ,
length : count , info : {
bytesRead : this . # bytesRead ,
dataLength : this . dataLength
}
} ) ;
}
2022-09-05 16:57:11 -04:00
# peekBytes ( offset , length , loose ) {
let alignedLength = Math . ceil ( length / exports . WordSize ) * exports . WordSize ;
if ( this . # offset + alignedLength > this . # data . length ) {
if ( this . allowLoose && loose && this . # offset + length <= this . # data . length ) {
alignedLength = length ;
}
else {
2022-11-09 02:57:02 -05:00
( 0 , index _js _1 . assert ) ( false , "data out-of-bounds" , "BUFFER_OVERRUN" , {
2022-09-15 22:58:45 -04:00
buffer : ( 0 , index _js _1 . getBytesCopy ) ( this . # data ) ,
2022-09-05 16:57:11 -04:00
length : this . # data . length ,
offset : this . # offset + alignedLength
} ) ;
}
}
return this . # data . slice ( this . # offset , this . # offset + alignedLength ) ;
}
// Create a sub-reader with the same underlying data, but offset
subReader ( offset ) {
2024-01-12 19:47:16 -05:00
const reader = new Reader ( this . # data . slice ( this . # offset + offset ) , this . allowLoose , this . # maxInflation ) ;
reader . # parent = this ;
return reader ;
2022-09-05 16:57:11 -04:00
}
// Read bytes
readBytes ( length , loose ) {
let bytes = this . # peekBytes ( 0 , length , ! ! loose ) ;
2024-01-12 19:47:16 -05:00
this . # incrementBytesRead ( length ) ;
2022-09-05 16:57:11 -04:00
this . # offset += bytes . length ;
// @TODO: Make sure the length..end bytes are all 0?
return bytes . slice ( 0 , length ) ;
}
// Read a numeric values
readValue ( ) {
2022-09-15 22:58:45 -04:00
return ( 0 , index _js _1 . toBigInt ) ( this . readBytes ( exports . WordSize ) ) ;
2022-09-05 16:57:11 -04:00
}
readIndex ( ) {
2022-09-15 22:58:45 -04:00
return ( 0 , index _js _1 . toNumber ) ( this . readBytes ( exports . WordSize ) ) ;
2022-09-05 16:57:11 -04:00
}
}
exports . Reader = Reader ;
//# sourceMappingURL=abstract-coder.js.map