Updated dist files.

This commit is contained in:
Richard Moore 2020-02-06 18:21:34 -05:00
parent a6b696d8bd
commit f9ab665b52
No known key found for this signature in database
GPG Key ID: 665176BE8E9DC651
105 changed files with 1533 additions and 876 deletions

@ -1 +1 @@
export declare const version = "abi/5.0.0-beta.144";
export declare const version = "abi/5.0.0-beta.145";

@ -1 +1 @@
export const version = "abi/5.0.0-beta.144";
export const version = "abi/5.0.0-beta.145";

@ -41,7 +41,7 @@ export declare class Interface {
};
readonly deploy: ConstructorFragment;
readonly _abiCoder: AbiCoder;
static _isInterface: boolean;
readonly _isInterface: boolean;
constructor(fragments: string | Array<Fragment | JsonFragment | string>);
format(format?: string): string | Array<string>;
static getAbiCoder(): AbiCoder;

@ -1 +1 @@
export declare const version = "abi/5.0.0-beta.144";
export declare const version = "abi/5.0.0-beta.145";

@ -1,3 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = "abi/5.0.0-beta.144";
exports.version = "abi/5.0.0-beta.145";

@ -41,7 +41,7 @@ export declare class Interface {
};
readonly deploy: ConstructorFragment;
readonly _abiCoder: AbiCoder;
static _isInterface: boolean;
readonly _isInterface: boolean;
constructor(fragments: string | Array<Fragment | JsonFragment | string>);
format(format?: string): string | Array<string>;
static getAbiCoder(): AbiCoder;

@ -31,7 +31,7 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"tarballHash": "0x6f0200ce3170d8149ac6b5a736d2bd519bfa6648d0a60ff773aafa47a17b1e85",
"tarballHash": "0xea1c770d87b34f15239a3ddbc5813d90e7627b57b9afa633997dfb903ca51efd",
"types": "./lib/index.d.ts",
"version": "5.0.0-beta.144"
"version": "5.0.0-beta.145"
}

@ -1 +1 @@
export const version = "abi/5.0.0-beta.144";
export const version = "abi/5.0.0-beta.145";

@ -1 +1 @@
export declare const version = "abstract-signer/5.0.0-beta.139";
export declare const version = "abstract-signer/5.0.0-beta.140";

@ -1 +1 @@
export const version = "abstract-signer/5.0.0-beta.139";
export const version = "abstract-signer/5.0.0-beta.140";

@ -122,25 +122,9 @@ export class Signer {
if (tx.nonce == null) {
tx.nonce = this.getTransactionCount("pending");
}
/*
// checkTransaction does this...
if (tx.from == null) {
tx.from = this.getAddress();
} else {
tx.from = Promise.all([
this.getAddress(),
this.provider.resolveName(tx.from)
]).then((results) => {
if (results[0] !== results[1]) {
logger.throwArgumentError("from address mismatch", "transaction", transaction);
}
return results[0];
});
}
*/
if (tx.gasLimit == null) {
tx.gasLimit = this.estimateGas(tx).catch((error) => {
logger.throwError("cannot estimate gas; transaction may fail or may require manual gas limit", Logger.errors.UNPREDICTABLE_GAS_LIMIT, {
return logger.throwError("cannot estimate gas; transaction may fail or may require manual gas limit", Logger.errors.UNPREDICTABLE_GAS_LIMIT, {
tx: tx
});
});

@ -1 +1 @@
export declare const version = "abstract-signer/5.0.0-beta.139";
export declare const version = "abstract-signer/5.0.0-beta.140";

@ -1,3 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = "abstract-signer/5.0.0-beta.139";
exports.version = "abstract-signer/5.0.0-beta.140";

@ -173,25 +173,9 @@ var Signer = /** @class */ (function () {
if (tx.nonce == null) {
tx.nonce = this.getTransactionCount("pending");
}
/*
// checkTransaction does this...
if (tx.from == null) {
tx.from = this.getAddress();
} else {
tx.from = Promise.all([
this.getAddress(),
this.provider.resolveName(tx.from)
]).then((results) => {
if (results[0] !== results[1]) {
logger.throwArgumentError("from address mismatch", "transaction", transaction);
}
return results[0];
});
}
*/
if (tx.gasLimit == null) {
tx.gasLimit = this.estimateGas(tx).catch(function (error) {
logger.throwError("cannot estimate gas; transaction may fail or may require manual gas limit", logger_1.Logger.errors.UNPREDICTABLE_GAS_LIMIT, {
return logger.throwError("cannot estimate gas; transaction may fail or may require manual gas limit", logger_1.Logger.errors.UNPREDICTABLE_GAS_LIMIT, {
tx: tx
});
});

@ -27,7 +27,7 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"tarballHash": "0xe2e6e52bcdae724519b652bf02b2c3779db22c7da7ce7af404c5a6b152a56fb0",
"tarballHash": "0x45c10e64c71e99997884ff59d59e6e3829043a155d8109bc2ee644efc40c3c43",
"types": "./lib/index.d.ts",
"version": "5.0.0-beta.139"
"version": "5.0.0-beta.140"
}

@ -1 +1 @@
export const version = "abstract-signer/5.0.0-beta.139";
export const version = "abstract-signer/5.0.0-beta.140";

@ -161,32 +161,15 @@ export abstract class Signer {
// By default called from: (overriding these prevents it)
// - sendTransaction
async populateTransaction(transaction: TransactionRequest): Promise<TransactionRequest> {
const tx = await resolveProperties(this.checkTransaction(transaction))
const tx: TransactionRequest = await resolveProperties(this.checkTransaction(transaction))
if (tx.to != null) { tx.to = Promise.resolve(tx.to).then((to) => this.resolveName(to)); }
if (tx.gasPrice == null) { tx.gasPrice = this.getGasPrice(); }
if (tx.nonce == null) { tx.nonce = this.getTransactionCount("pending"); }
/*
// checkTransaction does this...
if (tx.from == null) {
tx.from = this.getAddress();
} else {
tx.from = Promise.all([
this.getAddress(),
this.provider.resolveName(tx.from)
]).then((results) => {
if (results[0] !== results[1]) {
logger.throwArgumentError("from address mismatch", "transaction", transaction);
}
return results[0];
});
}
*/
if (tx.gasLimit == null) {
tx.gasLimit = this.estimateGas(tx).catch((error) => {
logger.throwError("cannot estimate gas; transaction may fail or may require manual gas limit", Logger.errors.UNPREDICTABLE_GAS_LIMIT, {
return logger.throwError("cannot estimate gas; transaction may fail or may require manual gas limit", Logger.errors.UNPREDICTABLE_GAS_LIMIT, {
tx: tx
});
});

@ -1 +1 @@
export declare const version = "asm/5.0.0-beta.148";
export declare const version = "asm/5.0.0-beta.149";

@ -1 +1 @@
export const version = "asm/5.0.0-beta.148";
export const version = "asm/5.0.0-beta.149";

@ -1,15 +1,16 @@
import { Opcode } from "./opcodes";
export declare type Location = {
offset: number;
line: number;
length: number;
source: string;
statement: boolean;
};
export declare type AssembleVisitFunc = (node: Node, bytecode: string) => void;
export declare type VisitFunc = (node: Node) => void;
export declare abstract class Node {
readonly tag: string;
readonly location: Location;
readonly warnings: Array<string>;
constructor(guard: any, location: Location, options: {
[key: string]: any;
});
@ -30,6 +31,12 @@ export declare class LiteralNode extends ValueNode {
assemble(assembler: Assembler, visit: AssembleVisitFunc): Promise<void>;
static from(options: any): LiteralNode;
}
export declare class PopNode extends ValueNode {
readonly index: number;
constructor(guard: any, location: Location, index: number);
readonly placeholder: string;
static from(options: any): PopNode;
}
export declare class LinkNode extends ValueNode {
readonly type: string;
readonly label: string;
@ -40,7 +47,8 @@ export declare class LinkNode extends ValueNode {
export declare class OpcodeNode extends ValueNode {
readonly opcode: Opcode;
readonly operands: Array<ValueNode>;
constructor(guard: any, location: Location, opcode: Opcode, operands: Array<ValueNode>);
readonly instructional: boolean;
constructor(guard: any, location: Location, opcode: Opcode, operands: Array<ValueNode>, instructional: boolean);
assemble(assembler: Assembler, visit: AssembleVisitFunc): Promise<void>;
children(): Array<Node>;
visit(visit: VisitFunc): void;
@ -61,7 +69,6 @@ export declare class DataNode extends LabelledNode {
constructor(guard: any, location: Location, name: string, data: string);
assemble(assembler: Assembler, visit: AssembleVisitFunc): Promise<void>;
children(): Array<Node>;
visit(visit: VisitFunc): void;
static from(options: any): DataNode;
}
export declare class EvaluationNode extends ValueNode {
@ -82,10 +89,8 @@ export declare class ScopeNode extends LabelledNode {
constructor(guard: any, location: Location, name: string, statements: Array<Node>);
assemble(assembler: Assembler, visit: AssembleVisitFunc): Promise<void>;
children(): Array<Node>;
visit(visit: VisitFunc): void;
static from(options: any): ScopeNode;
}
export declare function parse(code: string): Node;
export declare type Operation = {
opcode: Opcode;
offset: number;
@ -96,15 +101,15 @@ export interface Bytecode extends Array<Operation> {
}
export declare function disassemble(bytecode: string): Bytecode;
export declare function formatBytecode(bytecode: Array<Operation>): string;
interface DataSource extends Array<number> {
readonly offset: number;
export interface DataSource extends Array<number> {
offset: number;
ast: Node;
source: string;
}
declare type NodeState = {
export declare type NodeState = {
node: Node;
offset: number;
bytecode: string;
pending: string;
object?: number | DataSource;
};
export declare type AssemblerOptions = {
filename?: string;
@ -113,42 +118,46 @@ export declare type AssemblerOptions = {
defines?: {
[name: string]: any;
};
target?: string;
};
export declare type ParserOptions = {
ignoreWarnings?: boolean;
};
declare class Assembler {
readonly root: Node;
readonly positionIndependentCode: boolean;
readonly nodes: {
[tag: string]: NodeState;
};
readonly labels: {
[name: string]: LabelledNode;
};
readonly filename: string;
readonly positionIndependentCode: boolean;
readonly retry: number;
readonly defines: {
[name: string]: any;
_parents: {
[tag: string]: Node;
};
private _stack;
private _parents;
private _script;
private _changed;
constructor(root: Node, options: AssemblerOptions);
readonly changed: boolean;
getTarget(name: string): LabelledNode;
reset(): void;
constructor(root: Node, positionIndependentCode?: boolean);
getTarget(label: string): LabelledNode;
evaluate(script: string, source: Node): Promise<any>;
start(node: Node): void;
end(node: Node): void;
getPendingBytecode(node: Node): string;
_appendBytecode(bytecode: string): void;
getAncestor<T = Node>(node: Node, cls: {
new (...args: any[]): T;
}): T;
getOffset(node: Node, source?: Node): number;
setOffset(node: Node, offset: number): void;
getBytecode(node: Node): string;
setBytecode(node: Node, bytecode: string): void;
getLinkValue(target: LabelledNode, source: Node): number | DataSource;
get(name: string, source: Node): any;
_didChange(): void;
_assemble(): Promise<string>;
assemble(): Promise<string>;
start(node: Node): void;
end(node: Node): void;
}
export declare enum SemanticErrorSeverity {
error = "error",
warning = "warning"
}
export declare type SemanticError = {
readonly message: string;
readonly severity: SemanticErrorSeverity;
readonly node: Node;
};
export declare function parse(code: string, options?: ParserOptions): Node;
export declare function assemble(ast: Node, options?: AssemblerOptions): Promise<string>;
export {};

@ -9,10 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
});
};
// @TODO:
// - PIC
// - warn on opcode non-function iff parameters
// - warn return/revert non-empty, comment ; !assert(+1 @extra)
// - $$
// - In JS add config (positionIndependent)
// - When checking name collisions, verify no collision in javascript
import { dirname, resolve } from "path";
@ -118,9 +115,8 @@ export class Node {
throw new Error("cannot instantiate class");
}
logger.checkAbstract(new.target, Node);
ethers.utils.defineReadOnly(this, "location", location);
ethers.utils.defineReadOnly(this, "location", Object.freeze(location));
ethers.utils.defineReadOnly(this, "tag", `node-${nextTag++}-${this.constructor.name}`);
ethers.utils.defineReadOnly(this, "warnings", []);
for (const key in options) {
ethers.utils.defineReadOnly(this, key, options[key]);
}
@ -139,6 +135,9 @@ export class Node {
}
visit(visit) {
visit(this);
this.children().forEach((child) => {
child.visit(visit);
});
}
static from(options) {
const Factories = {
@ -151,6 +150,7 @@ export class Node {
length: LinkNode,
offset: LinkNode,
opcode: OpcodeNode,
pop: PopNode,
scope: ScopeNode,
};
const factory = Factories[options.type];
@ -160,14 +160,6 @@ export class Node {
return factory.from(options);
}
}
/*
export abstract class CodeNode extends Node {
constructor(guard: any, location: Location, options: { [ key: string ]: any }) {
logger.checkAbstract(new.target, CodeNode);
super(guard, location, options);
}
}
*/
export class ValueNode extends Node {
constructor(guard, location, options) {
logger.checkAbstract(new.target, ValueNode);
@ -215,6 +207,20 @@ export class LiteralNode extends ValueNode {
return new LiteralNode(Guard, options.loc, options.value, !!options.verbatim);
}
}
export class PopNode extends ValueNode {
constructor(guard, location, index) {
super(guard, location, { index });
}
get placeholder() {
if (this.index === 0) {
return "$$";
}
return "$" + String(this.index);
}
static from(options) {
return new PopNode(Guard, options.loc, options.index);
}
}
export class LinkNode extends ValueNode {
constructor(guard, location, type, label) {
super(guard, location, { type, label });
@ -223,28 +229,65 @@ export class LinkNode extends ValueNode {
return __awaiter(this, void 0, void 0, function* () {
assembler.start(this);
let value = null;
let isOffset = false;
const target = assembler.getTarget(this.label);
if (target instanceof LabelNode) {
if (this.type === "offset") {
//value = assembler.getOffset(this.label);
value = (assembler.getLinkValue(target, this));
isOffset = true;
}
}
else {
const result = (assembler.getLinkValue(target, this));
if (this.type === "offset") {
//value = assembler.getOffset(this.label);
value = result.offset;
isOffset = true;
}
else if (this.type === "length") {
//value = assembler.getLength(this.label);
value = result.length;
}
}
if (value == null) {
throw new Error("labels can only be targetted as offsets");
}
if (isOffset && assembler.positionIndependentCode) {
const here = assembler.getOffset(this, this);
const opcodes = [];
if (here > value) {
// Jump backwards
// Find a literal with length the encodes its own length in the delta
let literal = "0x";
for (let w = 1; w <= 5; w++) {
if (w > 4) {
throw new Error("jump too large!");
}
literal = pushLiteral(here - value + w);
if (ethers.utils.hexDataLength(literal) <= w) {
literal = ethers.utils.hexZeroPad(literal, w);
break;
}
}
opcodes.push(literal);
opcodes.push(Opcode.from("PC"));
opcodes.push(Opcode.from("SUB"));
// This also works, in case the above literal thing doesn't work out...
//opcodes.push(Opcode.from("PC"));
//opcodes.push(pushLiteral(-delta));
//opcodes.push(Opcode.from("SWAP1"));
//opcodes.push(Opcode.from("SUB"));
}
else {
// Jump forwards; this is easy to calculate since we can
// do PC firat.
opcodes.push(Opcode.from("PC"));
opcodes.push(pushLiteral(value - here));
opcodes.push(Opcode.from("ADD"));
}
visit(this, hexConcat(opcodes));
}
else {
visit(this, pushLiteral(value));
}
assembler.end(this);
});
}
@ -254,11 +297,8 @@ export class LinkNode extends ValueNode {
}
}
export class OpcodeNode extends ValueNode {
constructor(guard, location, opcode, operands) {
super(guard, location, { opcode, operands });
if (opcode.isPush()) {
this.warnings.push("the PUSH opcode modifies program flow - use literals instead");
}
constructor(guard, location, opcode, operands, instructional) {
super(guard, location, { instructional, opcode, operands });
}
assemble(assembler, visit) {
return __awaiter(this, void 0, void 0, function* () {
@ -289,23 +329,14 @@ export class OpcodeNode extends ValueNode {
if (!opcode) {
throw new Error("unknown opcode: " + options.mnemonic);
}
// Using the function syntax will check the operand count
if (!options.bare) {
if (opcode.mnemonic === "POP" && options.operands.length === 0) {
// This is ok... Pop has a delta of 0, but without operands
}
else if (options.operands.length !== opcode.delta) {
throw new Error(`opcode ${opcode.mnemonic} expects ${opcode.delta} operands`);
}
}
const operands = Object.freeze(options.operands.map((o) => {
const operand = Node.from(o);
if (!(operand instanceof ValueNode)) {
throw new Error("invalid operand");
throw new Error("bad grammar?!");
}
return operand;
}));
return new OpcodeNode(Guard, options.loc, opcode, operands);
return new OpcodeNode(Guard, options.loc, opcode, operands, !!options.bare);
}
}
export class LabelledNode extends Node {
@ -344,7 +375,7 @@ export class DataNode extends LabelledNode {
// We pad data if is contains PUSH opcodes that would overrun
// the data, which could eclipse valid operations (since the
// VM won't execute or jump within PUSH operations)
const bytecode = ethers.utils.arrayify(assembler.getPendingBytecode(this));
const bytecode = ethers.utils.arrayify(assembler.getBytecode(this));
// Replay the data as bytecode, skipping PUSH data
let i = 0;
while (i < bytecode.length) {
@ -365,12 +396,6 @@ export class DataNode extends LabelledNode {
children() {
return this.data;
}
visit(visit) {
visit(this);
for (let i = 0; i < this.data.length; i++) {
this.data[i].visit(visit);
}
}
static from(options) {
if (options.type !== "data") {
throw new Error("expected data type");
@ -442,12 +467,6 @@ export class ScopeNode extends LabelledNode {
children() {
return this.statements;
}
visit(visit) {
visit(this);
for (let i = 0; i < this.statements.length; i++) {
this.statements[i].visit(visit);
}
}
static from(options) {
if (options.type !== "scope") {
throw new Error("expected scope type");
@ -455,56 +474,6 @@ export class ScopeNode extends LabelledNode {
return new ScopeNode(Guard, options.loc, options.name, Object.freeze(options.statements.map((s) => Node.from(s))));
}
}
export function parse(code) {
// Since jison allows \n, \r or \r\n line endings, we need some
// twekaing to get the correct position
const lines = [];
let offset = 0;
code.split(/(\r\n?|\n)/g).forEach((clump, index) => {
if (index % 2) {
lines[lines.length - 1].line += clump;
}
else {
lines.push({ line: clump, offset: offset });
}
offset += clump.length;
});
// Add a mock-EOF to the end of the file so we don't out-of-bounds
// on the last character
if (lines.length) {
lines[lines.length - 1].line += "\n";
}
// Givens a line (1 offset) and column (0 offset) return the byte offset
const getOffset = function (line, column) {
const info = lines[line - 1];
if (!info || column >= info.line.length) {
throw new Error("out of range");
}
return info.offset + column;
};
// We use this in the _parser to convert locations to source
_parser.yy._ethersLocation = function (loc) {
// The _ scope should call with null to get the full source
if (loc == null) {
return Object.freeze({
offset: 0,
length: code.length,
source: code
});
}
const offset = getOffset(loc.first_line, loc.first_column);
const end = getOffset(loc.last_line, loc.last_column);
return Object.freeze({
offset: offset,
length: (end - offset),
source: code.substring(offset, end)
});
};
const result = Node.from(_parse(code));
// Nuke the source code lookup callback
_parser.yy._ethersLocation = null;
return result;
}
export function disassemble(bytecode) {
const ops = [];
const offsets = {};
@ -568,14 +537,10 @@ export function formatBytecode(bytecode) {
});
return lines.join("\n");
}
// @TODO: Rename to Assembler?
class Assembler {
constructor(root, options) {
ethers.utils.defineReadOnly(this, "positionIndependentCode", !!options.positionIndependentCode);
ethers.utils.defineReadOnly(this, "retry", ((options.retry != null) ? options.retry : 512));
ethers.utils.defineReadOnly(this, "filename", resolve(options.filename || "./contract.asm"));
ethers.utils.defineReadOnly(this, "defines", Object.freeze(options.defines || {}));
constructor(root, positionIndependentCode) {
ethers.utils.defineReadOnly(this, "root", root);
ethers.utils.defineReadOnly(this, "positionIndependentCode", !!positionIndependentCode);
const nodes = {};
const labels = {};
const parents = {};
@ -584,8 +549,7 @@ class Assembler {
nodes[node.tag] = {
node: node,
offset: 0x0,
bytecode: "0x",
pending: "0x"
bytecode: "0x"
};
if (node instanceof LabelledNode) {
// Check for duplicate labels
@ -611,52 +575,14 @@ class Assembler {
ethers.utils.defineReadOnly(this, "labels", Object.freeze(labels));
ethers.utils.defineReadOnly(this, "nodes", Object.freeze(nodes));
ethers.utils.defineReadOnly(this, "_parents", Object.freeze(parents));
ethers.utils.defineReadOnly(this, "_stack", []);
this.reset();
}
get changed() {
return this._changed;
}
// Link operations
getTarget(name) {
return this.labels[name];
}
// Reset the assmebler for another run with updated values
reset() {
this._changed = false;
for (const tag in this.nodes) {
delete this.nodes[tag].object;
}
this._script = new Script(this.filename, (name, context) => {
return this.get(name, context);
});
getTarget(label) {
return this.labels[label];
}
// Evaluate script in the context of a {{! }} or {{= }}
evaluate(script, source) {
return this._script.evaluate(script, source);
}
start(node) {
this._stack.push(node);
const info = this.nodes[node.tag];
info.pending = "0x";
}
end(node) {
if (this._stack.pop() !== node) {
throw new Error("missing push/pop pair");
}
const info = this.nodes[node.tag];
if (info.pending !== info.bytecode) {
this._didChange();
}
info.bytecode = info.pending;
}
getPendingBytecode(node) {
return this.nodes[node.tag].pending;
}
_appendBytecode(bytecode) {
this._stack.forEach((node) => {
const info = this.nodes[node.tag];
info.pending = hexConcat([info.pending, bytecode]);
});
return Promise.resolve(new Uint8Array(0));
}
getAncestor(node, cls) {
node = this._parents[node.tag];
@ -668,6 +594,23 @@ class Assembler {
}
return null;
}
getOffset(node, source) {
const offset = this.nodes[node.tag].offset;
if (source == null) {
return offset;
}
const sourceScope = ((source instanceof ScopeNode) ? source : this.getAncestor(source, ScopeNode));
return offset - this.nodes[sourceScope.tag].offset;
}
setOffset(node, offset) {
this.nodes[node.tag].offset = offset;
}
getBytecode(node) {
return this.nodes[node.tag].bytecode;
}
setBytecode(node, bytecode) {
this.nodes[node.tag].bytecode = bytecode;
}
getLinkValue(target, source) {
const sourceScope = ((source instanceof ScopeNode) ? source : this.getAncestor(source, ScopeNode));
const targetScope = ((target instanceof ScopeNode) ? target : this.getAncestor(target, ScopeNode));
@ -678,13 +621,7 @@ class Assembler {
throw new Error(`cannot access ${target.name} from ${source.tag}`);
}
// Return the offset relative to its scope
let offset = this.nodes[target.tag].offset - this.nodes[targetScope.tag].offset;
// Offsets are wrong; but we should finish this run and then try again
if (offset < 0) {
offset = 0;
this._didChange();
}
return offset;
return this.nodes[target.tag].offset - this.nodes[targetScope.tag].offset;
}
const info = this.nodes[target.tag];
// Return the offset is relative to its scope
@ -713,14 +650,209 @@ class Assembler {
// been marked as invalid, in which case accessing it will fail
if (safeOffset) {
bytes.offset = info.offset - this.nodes[sourceScope.tag].offset;
// Offsets are wqrong; but we should finish this run and then try again
if (bytes.offset < 0) {
bytes.offset = 0;
}
return bytes;
}
start(node) { }
end(node) { }
}
export var SemanticErrorSeverity;
(function (SemanticErrorSeverity) {
SemanticErrorSeverity["error"] = "error";
SemanticErrorSeverity["warning"] = "warning";
})(SemanticErrorSeverity || (SemanticErrorSeverity = {}));
;
// This Assembler is designed to only check for errors and warnings
// Warnings
// - Bare PUSH opcodes
// - Instructional opcode that has parameters
// Errors
// - Using a $$ outside of RPN
// - Using a $$ when it is not adjacent to the stack
// - The operand count does not match the opcode
// - An opcode is used as an operand but does not return a value
class SemanticChecker extends Assembler {
check() {
const errors = [];
this.root.visit((node) => {
if (node instanceof OpcodeNode) {
const opcode = node.opcode;
if (node.instructional) {
if (opcode.delta) {
errors.push({
message: `${opcode.mnemonic} used as instructional`,
severity: SemanticErrorSeverity.warning,
node: node
});
}
}
else {
if (opcode.mnemonic === "POP") {
if (node.operands.length !== 0) {
errors.push({
message: "POP expects 0 operands",
severity: SemanticErrorSeverity.error,
node: node
});
}
}
else if (node.operands.length !== opcode.delta) {
errors.push({
message: `${opcode.mnemonic} expects ${opcode.delta} operands`,
severity: SemanticErrorSeverity.error,
node: node
});
}
}
if (opcode.isPush()) {
// A stray PUSH operation will gobble up the following code
// bytes which is bad. But this may be a disassembled program
// and that PUSH may actually be just some data (which is safe)
errors.push({
message: "PUSH opcode modifies program flow - use literals instead",
severity: SemanticErrorSeverity.warning,
node: node
});
}
else if (!node.location.statement && opcode.alpha !== 1) {
// If an opcode does not push anything on the stack, it
// cannot be used as an operand
errors.push({
message: `${node.opcode.mnemonic} cannot be an operand`,
severity: SemanticErrorSeverity.error,
node: node
});
}
}
if (node.location.statement) {
if (node instanceof PopNode) {
// $$ by istelf is useless and is intended to be an operand
errors.push({
message: `$$ must be an operand`,
severity: SemanticErrorSeverity.error,
node: node
});
}
else {
const scope = this.getAncestor(node, ScopeNode);
// Make sure any $$ is stack adjacent (within this scope)
const ordered = [];
node.visit((node) => {
if (scope !== this.getAncestor(node, ScopeNode)) {
return;
}
ordered.push(node);
});
// Allow any number of stack adjacent $$
let foundZero = null;
let lastIndex = 0;
while (ordered.length && ordered[0] instanceof PopNode) {
const popNode = (ordered.shift());
const index = popNode.index;
if (index === 0) {
foundZero = popNode;
}
else if (index !== lastIndex + 1) {
errors.push({
message: `out-of-order stack placeholder ${popNode.placeholder}; expected $$${lastIndex + 1}`,
severity: SemanticErrorSeverity.error,
node: popNode
});
while (ordered.length && ordered[0] instanceof PopNode) {
ordered.shift();
}
break;
}
else {
lastIndex = index;
}
}
if (foundZero && lastIndex > 0) {
errors.push({
message: "cannot mix $$ and $1 stack placeholder",
severity: SemanticErrorSeverity.error,
node: foundZero
});
}
// If there are still any buried, we have a problem
const pops = ordered.filter((n) => (n instanceof PopNode));
if (pops.length) {
errors.push({
message: `stack placeholder ${(pops[0]).placeholder} must be stack adjacent`,
severity: SemanticErrorSeverity.error,
node: pops[0]
});
}
}
}
});
return errors;
}
}
class CodeGenerationAssembler extends Assembler {
constructor(root, options) {
super(root, !!options.positionIndependentCode);
ethers.utils.defineReadOnly(this, "retry", ((options.retry != null) ? options.retry : 512));
ethers.utils.defineReadOnly(this, "filename", resolve(options.filename || "./contract.asm"));
ethers.utils.defineReadOnly(this, "defines", Object.freeze(options.defines || {}));
ethers.utils.defineReadOnly(this, "_stack", []);
this.reset();
}
_didChange() {
this._changed = true;
}
get changed() {
return this._changed;
}
// Reset the assmebler for another run with updated values
reset() {
this._changed = false;
this._oldBytecode = {};
this._objectCache = {};
this._script = new Script(this.filename, (name, context) => {
return this.get(name, context);
});
}
evaluate(script, source) {
return this._script.evaluate(script, source);
}
getLinkValue(target, source) {
// Since we are iteratively generating code, offsets and lengths
// may not be stable at any given point in time, so if an offset
// is negative the code is obviously wrong, however we set it to
// 0 so we can proceed with generation to fill in as many blanks
// as possible; then we will try assembling again
const result = super.getLinkValue(target, source);
if (typeof (result) === "number") {
if (result < 0) {
this._didChange();
return 0;
}
return result;
}
if (result.offset < 0) {
result.offset = 0;
this._didChange();
}
return result;
}
start(node) {
this._stack.push(node);
this._oldBytecode[node.tag] = this.getBytecode(node);
this.setBytecode(node, "0x");
}
end(node) {
if (this._stack.pop() !== node) {
throw new Error("missing push/pop pair");
}
if (this._oldBytecode[node.tag] !== this.getBytecode(node)) {
this._didChange();
}
}
return Object.freeze(bytes);
}
// This is used by evaluate to access properties in JavaScript
// - "defines" allow meta-programming values to be used
// - jump destinations are available as numbers
// - bytecode and data are available as an immuatble DataSource
get(name, source) {
if (name === "defines") {
return this.defines;
@ -729,67 +861,137 @@ class Assembler {
if (!node) {
return undefined;
}
const info = this.nodes[node.tag];
if (info.object == null) {
info.object = this.getLinkValue(node, source);
// We cache objects when they are generated so all nodes
// receive consistent data; if there is a change we will
// run the entire assembly process again with the updated
// values
if (this._objectCache[node.tag] == null) {
this._objectCache[node.tag] = Object.freeze(this.getLinkValue(node, source));
}
return info.object;
}
_didChange() {
this._changed = true;
return this._objectCache[node.tag];
}
_assemble() {
return __awaiter(this, void 0, void 0, function* () {
let offset = 0;
const bytecodes = [];
yield this.root.assemble(this, (node, bytecode) => {
const state = this.nodes[node.tag];
// Things have moved; we will need to try again
if (state.offset !== offset) {
state.offset = offset;
if (this.getOffset(node) !== offset) {
this.setOffset(node, offset);
this._didChange();
}
this._appendBytecode(bytecode);
bytecodes.push(bytecode);
// The bytecode has changed; we will need to try again
//if (state.bytecode !== bytecode) {
// state.bytecode = bytecode;
// this._didChange();
//}
this._stack.forEach((node) => {
this.setBytecode(node, hexConcat([
this.getBytecode(node),
bytecode
]));
});
offset += ethers.utils.hexDataLength(bytecode);
});
return hexConcat(bytecodes);
});
}
assemble() {
assemble(label) {
return __awaiter(this, void 0, void 0, function* () {
if (label == null) {
label = "_";
}
const target = this.getTarget(label);
if (!target) {
logger.throwArgumentError(`unknown labelled target: ${label}`, "label", label);
}
else if (!(target instanceof ScopeNode || target instanceof DataNode)) {
logger.throwArgumentError(`cannot assemble a bodyless label: ${label}`, "label", label);
}
// Continue re-evaluating the bytecode until a stable set of
// offsets, length and values are reached.
let bytecode = yield this._assemble();
yield this._assemble();
for (let i = 0; i < this.retry; i++) {
// Regenerate the code with the updated assembler values
this.reset();
const adjusted = yield this._assemble();
yield this._assemble();
// Generated bytecode is stable!! :)
if (!this.changed) {
console.log(`Assembled in ${i} attempts`);
return bytecode;
}
// Try again...
bytecode = adjusted;
}
// This should not happen; something is wrong with the grammar
// or missing enter/exit call in assemble
if (this._stack.length !== 0) {
throw new Error("bad AST");
throw new Error("Bad AST! Bad grammar?!");
}
//console.log(`Assembled in ${ i } attempts`);
return this.getBytecode(target);
;
}
}
return logger.throwError(`unable to assemble; ${this.retry} attempts failed to generate stable bytecode`, ethers.utils.Logger.errors.UNKNOWN_ERROR, {});
});
}
}
export function parse(code, options) {
if (options == null) {
options = {};
}
// Since jison allows \n, \r or \r\n line endings, we need some
// twekaing to get the correct position
const lines = [];
let offset = 0;
code.split(/(\r\n?|\n)/g).forEach((clump, index) => {
if (index % 2) {
lines[lines.length - 1].line += clump;
}
else {
lines.push({ line: clump, offset: offset });
}
offset += clump.length;
});
// Add a mock-EOF to the end of the file so we don't out-of-bounds
// on the last character
if (lines.length) {
lines[lines.length - 1].line += "\n";
}
// Givens a line (1 offset) and column (0 offset) return the byte offset
const getOffset = function (line, column) {
const info = lines[line - 1];
if (!info || column >= info.line.length) {
throw new Error("out of range");
}
return info.offset + column;
};
// We use this in the _parser to convert locations to source
_parser.yy._ethersLocation = function (loc) {
// The _ scope should call with null to get the full source
if (loc == null) {
return {
offset: 0,
line: 0,
length: code.length,
source: code,
statement: true
};
}
const offset = getOffset(loc.first_line, loc.first_column);
const end = getOffset(loc.last_line, loc.last_column);
return {
offset: offset,
line: loc.first_line - 1,
length: (end - offset),
source: code.substring(offset, end),
statement: (!!loc.statement)
};
};
const result = Node.from(_parse(code));
// Nuke the source code lookup callback
_parser.yy._ethersLocation = null;
// Semantic Checks
const checker = new SemanticChecker(result);
const errors = checker.check();
if (errors.filter((e) => (e.severity === SemanticErrorSeverity.error)).length || (errors.length && !options.ignoreWarnings)) {
const error = new Error("semantic errors during parsing");
error.errors = errors;
throw error;
}
return result;
}
export function assemble(ast, options) {
return __awaiter(this, void 0, void 0, function* () {
const assembler = new Assembler(ast, options || {});
return assembler.assemble();
const assembler = new CodeGenerationAssembler(ast, options || {});
return assembler.assemble(options.target || "_");
});
}

@ -1,4 +1,4 @@
import { assemble, DataNode, disassemble, EvaluationNode, ExecutionNode, formatBytecode, LabelNode, LabelledNode, LinkNode, LiteralNode, Node, OpcodeNode, parse, ScopeNode, ValueNode } from "./assembler";
import { assemble, DataNode, disassemble, EvaluationNode, ExecutionNode, formatBytecode, LabelNode, LabelledNode, LinkNode, LiteralNode, Node, OpcodeNode, parse, PopNode, ScopeNode, ValueNode } from "./assembler";
import { Opcode } from "./opcodes";
import { AssemblerOptions, AssembleVisitFunc, Bytecode, Location, Operation, VisitFunc } from "./assembler";
export { Opcode, assemble, disassemble, formatBytecode, parse, DataNode, EvaluationNode, ExecutionNode, LabelNode, LabelledNode, LinkNode, LiteralNode, Node, OpcodeNode, ScopeNode, ValueNode, AssemblerOptions, AssembleVisitFunc, Bytecode, Location, Operation, VisitFunc, };
import { AssemblerOptions, AssembleVisitFunc, Bytecode, Location, Operation, ParserOptions, SemanticError, SemanticErrorSeverity, VisitFunc } from "./assembler";
export { Opcode, assemble, disassemble, formatBytecode, parse, DataNode, EvaluationNode, ExecutionNode, LabelNode, LabelledNode, LinkNode, LiteralNode, Node, OpcodeNode, PopNode, ScopeNode, ValueNode, AssemblerOptions, AssembleVisitFunc, Bytecode, Location, Operation, SemanticError, SemanticErrorSeverity, ParserOptions, VisitFunc, };

@ -1,10 +1,11 @@
"use strict";
import { assemble, DataNode, disassemble, EvaluationNode, ExecutionNode, formatBytecode, LabelNode, LabelledNode, LinkNode, LiteralNode, Node, OpcodeNode, parse, ScopeNode, ValueNode } from "./assembler";
import { assemble, DataNode, disassemble, EvaluationNode, ExecutionNode, formatBytecode, LabelNode, LabelledNode, LinkNode, LiteralNode, Node, OpcodeNode, parse, PopNode, ScopeNode, ValueNode } from "./assembler";
import { Opcode } from "./opcodes";
import { SemanticErrorSeverity } from "./assembler";
export {
// Opcodes
Opcode,
// Assembler functions
assemble, disassemble, formatBytecode, parse,
// Assembly AST Nodes
DataNode, EvaluationNode, ExecutionNode, LabelNode, LabelledNode, LinkNode, LiteralNode, Node, OpcodeNode, ScopeNode, ValueNode, };
DataNode, EvaluationNode, ExecutionNode, LabelNode, LabelledNode, LinkNode, LiteralNode, Node, OpcodeNode, PopNode, ScopeNode, ValueNode, SemanticErrorSeverity, };

@ -1,3 +1,8 @@
export declare enum OpcodeMemoryAccess {
write = "write",
read = "read",
full = "full"
}
export declare class Opcode {
readonly value: number;
readonly mnemonic: string;
@ -8,7 +13,7 @@ export declare class Opcode {
isJump(): boolean;
isValidJumpDest(): boolean;
isPush(): number;
isMemory(readOrWrite?: boolean): boolean;
isMemoryAccess(readOrWrite?: boolean): OpcodeMemoryAccess;
isStatic(): boolean;
static from(valueOrMnemonic: number | string): Opcode;
}

@ -8,6 +8,13 @@
// EXTCODEHASH
// See: https://eips.ethereum.org/EIPS/eip-1052
import { ethers } from "ethers";
export var OpcodeMemoryAccess;
(function (OpcodeMemoryAccess) {
OpcodeMemoryAccess["write"] = "write";
OpcodeMemoryAccess["read"] = "read";
OpcodeMemoryAccess["full"] = "full";
})(OpcodeMemoryAccess || (OpcodeMemoryAccess = {}));
;
export class Opcode {
constructor(mnemonic, value, delta, alpha, doc) {
ethers.utils.defineReadOnly(this, "mnemonic", mnemonic);
@ -31,12 +38,19 @@ export class Opcode {
return 0;
}
// Returns true if this operation writes to memory contents (or if readOrWrite, reads memory)
isMemory(readOrWrite) {
throw new Error("@TODO: return true if modifies memory");
// Unknown opcodes return null
isMemoryAccess(readOrWrite) {
switch ((_Opcodes[this.mnemonic.toLowerCase()] || { memory: null }).memory) {
case "read": return OpcodeMemoryAccess.read;
case "write": return OpcodeMemoryAccess.write;
case "full": return OpcodeMemoryAccess.full;
}
return null;
}
// Returns true if this opcode does not affect state
// Unknown opcodes return false
isStatic() {
throw new Error("@TODO: return true if certain non-state-changing");
return !(_Opcodes[this.mnemonic.toLowerCase()] || { nonStatic: true }).nonStatic;
}
static from(valueOrMnemonic) {
if (typeof (valueOrMnemonic) === "string") {
@ -75,7 +89,7 @@ const _Opcodes = {
shr: { value: 0x1c, delta: 2, alpha: 1, doc: "v = shr(shiftBits, value)" },
sar: { value: 0x1d, delta: 2, alpha: 1, doc: "v = sar(shiftBits, value)" },
// SHA3
sha3: { value: 0x20, delta: 2, alpha: 1, doc: "v = sha3(offset, length)" },
sha3: { value: 0x20, delta: 2, alpha: 1, doc: "v = sha3(offset, length)", memory: "read" },
// Environmental Information
address: { value: 0x30, delta: 0, alpha: 1, doc: "myAddr = address" },
balance: { value: 0x31, delta: 1, alpha: 1, doc: "wei = balance(address)" },
@ -84,14 +98,14 @@ const _Opcodes = {
callvalue: { value: 0x34, delta: 0, alpha: 1, doc: "msgValue = callvalue" },
calldataload: { value: 0x35, delta: 1, alpha: 1, doc: "calldataWordValue = calldataload(byteOffet)" },
calldatasize: { value: 0x36, delta: 0, alpha: 1, doc: "calldataLength = calldatasize" },
calldatacopy: { value: 0x37, delta: 3, alpha: 0, doc: "calldatacopy(dstMemoryIndex, dataIndex, length)" },
calldatacopy: { value: 0x37, delta: 3, alpha: 0, doc: "calldatacopy(dstMemoryIndex, dataIndex, length)", memory: "write" },
codesize: { value: 0x38, delta: 0, alpha: 1, doc: "myCodeLength = codesize" },
codecopy: { value: 0x39, delta: 3, alpha: 0, doc: "codecopy(dstMemoryIndex, codeIndex, length)" },
codecopy: { value: 0x39, delta: 3, alpha: 0, doc: "codecopy(dstMemoryIndex, codeIndex, length)", memory: "write" },
gasprice: { value: 0x3a, delta: 0, alpha: 1, doc: "txGasPrice = gasprice" },
extcodesize: { value: 0x3b, delta: 1, alpha: 1, doc: "otherCodeLength = extcodesize(address)" },
extcodecopy: { value: 0x3c, delta: 4, alpha: 0, doc: "extcodecopy(address, dstMemoryIndex, extcodeIndex, length)" },
extcodecopy: { value: 0x3c, delta: 4, alpha: 0, doc: "extcodecopy(address, dstMemoryIndex, extcodeIndex, length)", memory: "write" },
returndatasize: { value: 0x3d, delta: 0, alpha: 1, doc: "v = returndatasize" },
returndatacopy: { value: 0x3e, delta: 3, alpha: 0, doc: "returndatacopy(dstMemoryOffset, returndataIndex, length)" },
returndatacopy: { value: 0x3e, delta: 3, alpha: 0, doc: "returndatacopy(dstMemoryOffset, returndataIndex, length)", memory: "write" },
extcodehash: { value: 0x3f, delta: 1, alpha: 1, doc: "hash = extcodehash(address)" },
// Block Information
blockhash: { value: 0x40, delta: 1, alpha: 1, doc: "hash = blockhash(blockNumber)" },
@ -102,11 +116,11 @@ const _Opcodes = {
gaslimit: { value: 0x45, delta: 0, alpha: 1, doc: "gas = gaslimit" },
// Stack, Memory, Storage and Flow Operations
pop: { value: 0x50, delta: 1, alpha: 0, doc: "stackTopValue = pop" },
mload: { value: 0x51, delta: 1, alpha: 1, doc: "memoryWordValue = mload(memoryByteIndex)" },
mstore: { value: 0x52, delta: 2, alpha: 0, doc: "mstore(memoryByteIndex, valueOut)" },
mstore8: { value: 0x53, delta: 2, alpha: 0, doc: "mstore8(memoryByteIndex, valueOut [ & 0xff ])" },
mload: { value: 0x51, delta: 1, alpha: 1, doc: "memoryWordValue = mload(memoryByteIndex)", memory: "read" },
mstore: { value: 0x52, delta: 2, alpha: 0, doc: "mstore(memoryByteIndex, valueOut)", memory: "write" },
mstore8: { value: 0x53, delta: 2, alpha: 0, doc: "mstore8(memoryByteIndex, valueOut [ & 0xff ])", memory: "write" },
sload: { value: 0x54, delta: 1, alpha: 1, doc: "storageWordValue = sload(storageWordIndex)" },
sstore: { value: 0x55, delta: 2, alpha: 0, doc: "sstore(storageWordIndex, valueOut)" },
sstore: { value: 0x55, delta: 2, alpha: 0, doc: "sstore(storageWordIndex, valueOut)", nonStatic: true },
jump: { value: 0x56, delta: 1, alpha: 0, doc: "jump(target)" },
jumpi: { value: 0x57, delta: 2, alpha: 0, doc: "jumpi(target, notZero)" },
pc: { value: 0x58, delta: 0, alpha: 1, doc: "programCounter = pc" },
@ -181,22 +195,22 @@ const _Opcodes = {
swap15: { value: 0x9e, delta: 0, alpha: 0 },
swap16: { value: 0x9f, delta: 0, alpha: 0 },
// Loggin Operations
log0: { value: 0xa0, delta: 2, alpha: 0 },
log1: { value: 0xa1, delta: 3, alpha: 0 },
log2: { value: 0xa2, delta: 4, alpha: 0 },
log3: { value: 0xa3, delta: 5, alpha: 0 },
log4: { value: 0xa4, delta: 6, alpha: 0 },
log0: { value: 0xa0, delta: 2, alpha: 0, nonStatic: true, memory: "read" },
log1: { value: 0xa1, delta: 3, alpha: 0, nonStatic: true, memory: "read" },
log2: { value: 0xa2, delta: 4, alpha: 0, nonStatic: true, memory: "read" },
log3: { value: 0xa3, delta: 5, alpha: 0, nonStatic: true, memory: "read" },
log4: { value: 0xa4, delta: 6, alpha: 0, nonStatic: true, memory: "read" },
// System Operations
create: { value: 0xf0, delta: 3, alpha: 1, doc: "address = create(value, index, length)" },
call: { value: 0xf1, delta: 7, alpha: 1, doc: "v = call(gasLimit, address, value, inputIndex, inputLength, outputIndex, outputLength)" },
callcode: { value: 0xf2, delta: 7, alpha: 1, doc: "v = callcode(@TODO)" },
"return": { value: 0xf3, delta: 2, alpha: 0, doc: "return(index, length)" },
delegatecall: { value: 0xf4, delta: 6, alpha: 1, doc: "v = delegatecall(gasLimit, address, inputIndex, inputLength, outputIndex, outputLength)" },
create2: { value: 0xf5, delta: 4, alpha: 1, doc: "address = create2(value, index, length, salt)" },
staticcall: { value: 0xfa, delta: 6, alpha: 1, doc: "v = staticcall(gasLimit, address, inputIndex, inputLength, outputIndex, outputLength)" },
revert: { value: 0xfd, delta: 2, alpha: 0, doc: "revert(returnDataOffset, returnDataLength)" },
create: { value: 0xf0, delta: 3, alpha: 1, doc: "address = create(value, index, length)", nonStatic: true, memory: "read" },
call: { value: 0xf1, delta: 7, alpha: 1, doc: "v = call(gasLimit, address, value, inputIndex, inputLength, outputIndex, outputLength)", nonStatic: true, memory: "full" },
callcode: { value: 0xf2, delta: 7, alpha: 1, doc: "v = callcode(@TODO)", nonStatic: true, memory: "full" },
"return": { value: 0xf3, delta: 2, alpha: 0, doc: "return(index, length)", memory: "read" },
delegatecall: { value: 0xf4, delta: 6, alpha: 1, doc: "v = delegatecall(gasLimit, address, inputIndex, inputLength, outputIndex, outputLength)", nonStatic: true, memory: "full" },
create2: { value: 0xf5, delta: 4, alpha: 1, doc: "address = create2(value, index, length, salt)", nonStatic: true, memory: "read" },
staticcall: { value: 0xfa, delta: 6, alpha: 1, doc: "v = staticcall(gasLimit, address, inputIndex, inputLength, outputIndex, outputLength)", memory: "full" },
revert: { value: 0xfd, delta: 2, alpha: 0, doc: "revert(returnDataOffset, returnDataLength)", memory: "read" },
invalid: { value: 0xfe, delta: 0, alpha: 0, doc: "invalid" },
suicide: { value: 0xff, delta: 1, alpha: 0, doc: "suicide(targetAddress)" },
suicide: { value: 0xff, delta: 1, alpha: 0, doc: "suicide(targetAddress)", nonStatic: true },
};
const OpcodeMap = {};
const Opcodes = [];

@ -72,12 +72,12 @@
}
*/
var parser = (function(){
var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[2,19],$V1=[1,7],$V2=[1,8],$V3=[1,9],$V4=[1,10],$V5=[1,11],$V6=[1,12],$V7=[1,5],$V8=[1,6],$V9=[5,25],$Va=[5,11,14,15,16,17,18,22,25,28],$Vb=[2,2],$Vc=[1,19],$Vd=[5,10,11,13,14,15,16,17,18,22,25,28],$Ve=[1,25],$Vf=[1,26],$Vg=[1,27],$Vh=[2,14],$Vi=[5,10,11,13,14,15,16,17,18,22,25,27,28],$Vj=[16,17,18,27];
var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[2,21],$V1=[1,7],$V2=[1,8],$V3=[1,9],$V4=[1,10],$V5=[1,11],$V6=[1,12],$V7=[1,13],$V8=[1,14],$V9=[1,5],$Va=[1,6],$Vb=[5,27],$Vc=[5,11,14,15,16,17,18,19,20,24,27,30],$Vd=[2,2],$Ve=[1,21],$Vf=[5,10,11,13,14,15,16,17,18,19,20,24,27,30],$Vg=[1,27],$Vh=[1,28],$Vi=[1,29],$Vj=[2,16],$Vk=[5,10,11,13,14,15,16,17,18,19,20,24,27,29,30],$Vl=[16,17,20,29];
var parser = {trace: function trace () { },
yy: {},
symbols_: {"error":2,"program":3,"statement_list":4,"EOF":5,"javascript":6,"SCRIPT_TOKEN":7,"opcode_list":8,"opcode":9,"COMMA":10,"ID":11,"OPEN_PAREN":12,"CLOSE_PAREN":13,"HASH_ID":14,"DOLLAR_ID":15,"HEX":16,"DECIMAL":17,"SCRIPT_EVAL":18,"hex_list":19,"hex":20,"statement":21,"AT_ID":22,"COLON":23,"OPEN_BRACE":24,"CLOSE_BRACE":25,"OPEN_BRACKET":26,"CLOSE_BRACKET":27,"SCRIPT_EXEC":28,"$accept":0,"$end":1},
terminals_: {2:"error",5:"EOF",7:"SCRIPT_TOKEN",10:"COMMA",11:"ID",12:"OPEN_PAREN",13:"CLOSE_PAREN",14:"HASH_ID",15:"DOLLAR_ID",16:"HEX",17:"DECIMAL",18:"SCRIPT_EVAL",22:"AT_ID",23:"COLON",24:"OPEN_BRACE",25:"CLOSE_BRACE",26:"OPEN_BRACKET",27:"CLOSE_BRACKET",28:"SCRIPT_EXEC"},
productions_: [0,[3,2],[6,0],[6,2],[8,1],[8,3],[9,1],[9,3],[9,4],[9,1],[9,1],[9,1],[9,1],[9,2],[19,0],[19,2],[20,1],[20,1],[20,2],[4,0],[4,2],[21,1],[21,2],[21,4],[21,4],[21,2]],
symbols_: {"error":2,"program":3,"statement_list":4,"EOF":5,"javascript":6,"SCRIPT_TOKEN":7,"opcode_list":8,"opcode":9,"COMMA":10,"ID":11,"OPEN_PAREN":12,"CLOSE_PAREN":13,"HASH_ID":14,"DOLLAR_ID":15,"HEX":16,"DECIMAL":17,"DOLLAR_DOLLAR":18,"DOLLAR_INDEX":19,"SCRIPT_EVAL":20,"hex_list":21,"hex":22,"statement":23,"AT_ID":24,"COLON":25,"OPEN_BRACE":26,"CLOSE_BRACE":27,"OPEN_BRACKET":28,"CLOSE_BRACKET":29,"SCRIPT_EXEC":30,"$accept":0,"$end":1},
terminals_: {2:"error",5:"EOF",7:"SCRIPT_TOKEN",10:"COMMA",11:"ID",12:"OPEN_PAREN",13:"CLOSE_PAREN",14:"HASH_ID",15:"DOLLAR_ID",16:"HEX",17:"DECIMAL",18:"DOLLAR_DOLLAR",19:"DOLLAR_INDEX",20:"SCRIPT_EVAL",24:"AT_ID",25:"COLON",26:"OPEN_BRACE",27:"CLOSE_BRACE",28:"OPEN_BRACKET",29:"CLOSE_BRACKET",30:"SCRIPT_EXEC"},
productions_: [0,[3,2],[6,0],[6,2],[8,1],[8,3],[9,1],[9,3],[9,4],[9,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,2],[21,0],[21,2],[22,1],[22,1],[22,2],[4,0],[4,2],[23,1],[23,2],[23,4],[23,4],[23,2]],
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
/* this == yyval */
@ -124,54 +124,69 @@ case 12:
this.$ = { type: "decimal", value: $$[$0], loc: getLoc(yy, _$[$0]) };
break;
case 13:
this.$ = { type: "eval", script: $$[$0], loc: getLoc(yy, _$[$0-1], _$[$0]) };
this.$ = { type: "pop", index: 0, loc: getLoc(yy, _$[$0]) };
break;
case 14: case 19:
this.$ = [ ];
case 14:
this.$ = { type: "pop", index: parseInt(($$[$0]).substring(1)), loc: getLoc(yy, _$[$0]) };
break;
case 15:
this.$ = { type: "eval", script: $$[$0], loc: getLoc(yy, _$[$0-1], _$[$0]) };
break;
case 16: case 21:
this.$ = [ ];
break;
case 17:
{
const hexes = $$[$0].slice();;
hexes.unshift($$[$0-1]);
this.$ = hexes;
}
break;
case 16:
case 18:
this.$ = { type: "hex", verbatim: true, value: $$[$0], loc: getLoc(yy, _$[$0]) };
break;
case 17:
case 19:
{
const value = parseInt($$[$0]);
if (value >= 256) { throw new Error("decimal data values must be single bytes"); }
this.$ = { type: "hex", verbatim: true, value: ("0x" + (value).toString(16)), loc: getLoc(yy, _$[$0]) };
let hex = (value).toString(16);
while (hex.length < 2) { hex = "0" + hex; }
this.$ = { type: "hex", verbatim: true, value: ("0x" + hex), loc: getLoc(yy, _$[$0]) };
}
break;
case 18:
case 20:
this.$ = { type: "eval", verbatim: true, script: $$[$0], loc: getLoc(yy, _$[$0-1], _$[$0]) };
break;
case 20:
case 22:
{
const statements = $$[$0].slice();
statements.unshift($$[$0-1]);
this.$ = statements;
}
break;
case 22:
this.$ = { type: "label", name: $$[$0-1].substring(1), loc: getLoc(yy, _$[$0-1], _$[$0]) };
break;
case 23:
this.$ = { type: "scope", name: $$[$0-3].substring(1), statements: $$[$0-1], loc: getLoc(yy, _$[$0-3], _$[$0]) };
{
const statement = $$[$0];
statement.loc.statement = true;
this.$ = statement;
}
break;
case 24:
this.$ = { type: "data", name: $$[$0-3].substring(1), data: $$[$0-1], loc: getLoc(yy, _$[$0-3], _$[$0]) };
this.$ = { type: "label", name: $$[$0-1].substring(1), loc: getLoc(yy, _$[$0-1], _$[$0], true) };
break;
case 25:
this.$ = { type: "exec", script: $$[$0], loc: getLoc(yy, _$[$0-1], _$[$0]) };
this.$ = { type: "scope", name: $$[$0-3].substring(1), statements: $$[$0-1], loc: getLoc(yy, _$[$0-3], _$[$0], true) };
break;
case 26:
this.$ = { type: "data", name: $$[$0-3].substring(1), data: $$[$0-1], loc: getLoc(yy, _$[$0-3], _$[$0], true) };
break;
case 27:
this.$ = { type: "exec", script: $$[$0], loc: getLoc(yy, _$[$0-1], _$[$0], true) };
break;
}
},
table: [{3:1,4:2,5:$V0,9:4,11:$V1,14:$V2,15:$V3,16:$V4,17:$V5,18:$V6,21:3,22:$V7,28:$V8},{1:[3]},{5:[1,13]},o($V9,$V0,{21:3,9:4,4:14,11:$V1,14:$V2,15:$V3,16:$V4,17:$V5,18:$V6,22:$V7,28:$V8}),o($Va,[2,21]),{23:[1,15],24:[1,16],26:[1,17]},o($Va,$Vb,{6:18,7:$Vc}),o($Vd,[2,6],{12:[1,20]}),o($Vd,[2,9]),o($Vd,[2,10]),o($Vd,[2,11]),o($Vd,[2,12]),o($Vd,$Vb,{6:21,7:$Vc}),{1:[2,1]},o($V9,[2,20]),o($Va,[2,22]),{4:22,9:4,11:$V1,14:$V2,15:$V3,16:$V4,17:$V5,18:$V6,21:3,22:$V7,25:$V0,28:$V8},{16:$Ve,17:$Vf,18:$Vg,19:23,20:24,27:$Vh},o($Va,[2,25]),o($Vi,$Vb,{6:28,7:$Vc}),{8:30,9:31,11:$V1,13:[1,29],14:$V2,15:$V3,16:$V4,17:$V5,18:$V6},o($Vd,[2,13]),{25:[1,32]},{27:[1,33]},{16:$Ve,17:$Vf,18:$Vg,19:34,20:24,27:$Vh},o($Vj,[2,16]),o($Vj,[2,17]),o($Vj,$Vb,{6:35,7:$Vc}),o($Vi,[2,3]),o($Vd,[2,7]),{13:[1,36]},{10:[1,37],13:[2,4]},o($Va,[2,23]),o($Va,[2,24]),{27:[2,15]},o($Vj,[2,18]),o($Vd,[2,8]),{8:38,9:31,11:$V1,14:$V2,15:$V3,16:$V4,17:$V5,18:$V6},{13:[2,5]}],
defaultActions: {13:[2,1],34:[2,15],38:[2,5]},
table: [{3:1,4:2,5:$V0,9:4,11:$V1,14:$V2,15:$V3,16:$V4,17:$V5,18:$V6,19:$V7,20:$V8,23:3,24:$V9,30:$Va},{1:[3]},{5:[1,15]},o($Vb,$V0,{23:3,9:4,4:16,11:$V1,14:$V2,15:$V3,16:$V4,17:$V5,18:$V6,19:$V7,20:$V8,24:$V9,30:$Va}),o($Vc,[2,23]),{25:[1,17],26:[1,18],28:[1,19]},o($Vc,$Vd,{6:20,7:$Ve}),o($Vf,[2,6],{12:[1,22]}),o($Vf,[2,9]),o($Vf,[2,10]),o($Vf,[2,11]),o($Vf,[2,12]),o($Vf,[2,13]),o($Vf,[2,14]),o($Vf,$Vd,{6:23,7:$Ve}),{1:[2,1]},o($Vb,[2,22]),o($Vc,[2,24]),{4:24,9:4,11:$V1,14:$V2,15:$V3,16:$V4,17:$V5,18:$V6,19:$V7,20:$V8,23:3,24:$V9,27:$V0,30:$Va},{16:$Vg,17:$Vh,20:$Vi,21:25,22:26,29:$Vj},o($Vc,[2,27]),o($Vk,$Vd,{6:30,7:$Ve}),{8:32,9:33,11:$V1,13:[1,31],14:$V2,15:$V3,16:$V4,17:$V5,18:$V6,19:$V7,20:$V8},o($Vf,[2,15]),{27:[1,34]},{29:[1,35]},{16:$Vg,17:$Vh,20:$Vi,21:36,22:26,29:$Vj},o($Vl,[2,18]),o($Vl,[2,19]),o($Vl,$Vd,{6:37,7:$Ve}),o($Vk,[2,3]),o($Vf,[2,7]),{13:[1,38]},{10:[1,39],13:[2,4]},o($Vc,[2,25]),o($Vc,[2,26]),{29:[2,17]},o($Vl,[2,20]),o($Vf,[2,8]),{8:40,9:33,11:$V1,14:$V2,15:$V3,16:$V4,17:$V5,18:$V6,19:$V7,20:$V8},{13:[2,5]}],
defaultActions: {15:[2,1],36:[2,17],40:[2,5]},
parseError: function parseError (str, hash) {
if (hash.recoverable) {
this.trace(str);
@ -320,7 +335,7 @@ parse: function parse(input) {
}};
function getLoc(yy, start, end) {
function getLoc(yy, start, end, statement) {
if (end == null) { end = start; }
let result = null;
@ -329,7 +344,8 @@ function getLoc(yy, start, end) {
first_line: start.first_line,
first_column: start.first_column,
last_line: end.last_line,
last_column: end.last_column
last_column: end.last_column,
statement: !!statement
};
}
@ -337,7 +353,7 @@ function getLoc(yy, start, end) {
return yy._ethersLocation(result);
}
return Object.freeze(result);
return result;
}
/* generated by jison-lex 0.3.4 */
@ -690,9 +706,9 @@ case 9:return "COMMA"
break;
case 10:return "AT_ID"
break;
case 11:return "HASH_ID"
case 11:return "DOLLAR_ID"
break;
case 12:return "DOLLAR_ID"
case 12:return "HASH_ID"
break;
case 13:return "OPEN_BRACE"
break;
@ -708,14 +724,18 @@ case 18:return "HEX"
break;
case 19:return "DECIMAL"
break;
case 20:return "EOF"
case 20:return "DOLLAR_DOLLAR"
break;
case 21:return "INVALID"
case 21:return "DOLLAR_INDEX"
break;
case 22:return "EOF"
break;
case 23:return "INVALID"
break;
}
},
rules: [/^(?:\{\{=)/,/^(?:\{\{!)/,/^(?:([^\}]|\n|\}[^}]))/,/^(?:\}\})/,/^(?:([;][^\n]*\n))/,/^(?:(\s+))/,/^(?:([A-Za-z][A-Za-z0-9]*))/,/^(?:\()/,/^(?:\))/,/^(?:,)/,/^(?:([@][A-Za-z][A-Za-z0-9]*))/,/^(?:([#][A-Za-z][A-Za-z0-9]*))/,/^(?:([$][A-Za-z][A-Za-z0-9]*))/,/^(?:\{)/,/^(?:\})/,/^(?::)/,/^(?:\[)/,/^(?:\])/,/^(?:(0x([0-9a-fA-F][0-9a-fA-F])*))/,/^(?:([1-9][0-9]*|0))/,/^(?:$)/,/^(?:)/],
conditions: {"script":{"rules":[2,3],"inclusive":false},"INITIAL":{"rules":[0,1,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21],"inclusive":true}}
rules: [/^(?:\{\{=)/,/^(?:\{\{!)/,/^(?:([^\}]|\n|\}[^}]))/,/^(?:\}\})/,/^(?:([;][^\n]*\n))/,/^(?:(\s+))/,/^(?:([A-Za-z][A-Za-z0-9]*))/,/^(?:\()/,/^(?:\))/,/^(?:,)/,/^(?:([@][A-Za-z][A-Za-z0-9]*))/,/^(?:([$](_|[A-Za-z][A-Za-z0-9]*)))/,/^(?:([#](_|[A-Za-z][A-Za-z0-9]*)))/,/^(?:\{)/,/^(?:\})/,/^(?::)/,/^(?:\[)/,/^(?:\])/,/^(?:(0x([0-9a-fA-F][0-9a-fA-F])*))/,/^(?:([1-9][0-9]*|0))/,/^(?:\$\$)/,/^(?:([$][1-9][0-9]*))/,/^(?:$)/,/^(?:)/],
conditions: {"script":{"rules":[2,3],"inclusive":false},"INITIAL":{"rules":[0,1,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],"inclusive":true}}
});
return lexer;
})();

@ -1 +1 @@
export declare const version = "asm/5.0.0-beta.148";
export declare const version = "asm/5.0.0-beta.149";

@ -1,3 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = "asm/5.0.0-beta.148";
exports.version = "asm/5.0.0-beta.149";

@ -1,15 +1,16 @@
import { Opcode } from "./opcodes";
export declare type Location = {
offset: number;
line: number;
length: number;
source: string;
statement: boolean;
};
export declare type AssembleVisitFunc = (node: Node, bytecode: string) => void;
export declare type VisitFunc = (node: Node) => void;
export declare abstract class Node {
readonly tag: string;
readonly location: Location;
readonly warnings: Array<string>;
constructor(guard: any, location: Location, options: {
[key: string]: any;
});
@ -30,6 +31,12 @@ export declare class LiteralNode extends ValueNode {
assemble(assembler: Assembler, visit: AssembleVisitFunc): Promise<void>;
static from(options: any): LiteralNode;
}
export declare class PopNode extends ValueNode {
readonly index: number;
constructor(guard: any, location: Location, index: number);
readonly placeholder: string;
static from(options: any): PopNode;
}
export declare class LinkNode extends ValueNode {
readonly type: string;
readonly label: string;
@ -40,7 +47,8 @@ export declare class LinkNode extends ValueNode {
export declare class OpcodeNode extends ValueNode {
readonly opcode: Opcode;
readonly operands: Array<ValueNode>;
constructor(guard: any, location: Location, opcode: Opcode, operands: Array<ValueNode>);
readonly instructional: boolean;
constructor(guard: any, location: Location, opcode: Opcode, operands: Array<ValueNode>, instructional: boolean);
assemble(assembler: Assembler, visit: AssembleVisitFunc): Promise<void>;
children(): Array<Node>;
visit(visit: VisitFunc): void;
@ -61,7 +69,6 @@ export declare class DataNode extends LabelledNode {
constructor(guard: any, location: Location, name: string, data: string);
assemble(assembler: Assembler, visit: AssembleVisitFunc): Promise<void>;
children(): Array<Node>;
visit(visit: VisitFunc): void;
static from(options: any): DataNode;
}
export declare class EvaluationNode extends ValueNode {
@ -82,10 +89,8 @@ export declare class ScopeNode extends LabelledNode {
constructor(guard: any, location: Location, name: string, statements: Array<Node>);
assemble(assembler: Assembler, visit: AssembleVisitFunc): Promise<void>;
children(): Array<Node>;
visit(visit: VisitFunc): void;
static from(options: any): ScopeNode;
}
export declare function parse(code: string): Node;
export declare type Operation = {
opcode: Opcode;
offset: number;
@ -96,15 +101,15 @@ export interface Bytecode extends Array<Operation> {
}
export declare function disassemble(bytecode: string): Bytecode;
export declare function formatBytecode(bytecode: Array<Operation>): string;
interface DataSource extends Array<number> {
readonly offset: number;
export interface DataSource extends Array<number> {
offset: number;
ast: Node;
source: string;
}
declare type NodeState = {
export declare type NodeState = {
node: Node;
offset: number;
bytecode: string;
pending: string;
object?: number | DataSource;
};
export declare type AssemblerOptions = {
filename?: string;
@ -113,42 +118,46 @@ export declare type AssemblerOptions = {
defines?: {
[name: string]: any;
};
target?: string;
};
export declare type ParserOptions = {
ignoreWarnings?: boolean;
};
declare class Assembler {
readonly root: Node;
readonly positionIndependentCode: boolean;
readonly nodes: {
[tag: string]: NodeState;
};
readonly labels: {
[name: string]: LabelledNode;
};
readonly filename: string;
readonly positionIndependentCode: boolean;
readonly retry: number;
readonly defines: {
[name: string]: any;
_parents: {
[tag: string]: Node;
};
private _stack;
private _parents;
private _script;
private _changed;
constructor(root: Node, options: AssemblerOptions);
readonly changed: boolean;
getTarget(name: string): LabelledNode;
reset(): void;
constructor(root: Node, positionIndependentCode?: boolean);
getTarget(label: string): LabelledNode;
evaluate(script: string, source: Node): Promise<any>;
start(node: Node): void;
end(node: Node): void;
getPendingBytecode(node: Node): string;
_appendBytecode(bytecode: string): void;
getAncestor<T = Node>(node: Node, cls: {
new (...args: any[]): T;
}): T;
getOffset(node: Node, source?: Node): number;
setOffset(node: Node, offset: number): void;
getBytecode(node: Node): string;
setBytecode(node: Node, bytecode: string): void;
getLinkValue(target: LabelledNode, source: Node): number | DataSource;
get(name: string, source: Node): any;
_didChange(): void;
_assemble(): Promise<string>;
assemble(): Promise<string>;
start(node: Node): void;
end(node: Node): void;
}
export declare enum SemanticErrorSeverity {
error = "error",
warning = "warning"
}
export declare type SemanticError = {
readonly message: string;
readonly severity: SemanticErrorSeverity;
readonly node: Node;
};
export declare function parse(code: string, options?: ParserOptions): Node;
export declare function assemble(ast: Node, options?: AssemblerOptions): Promise<string>;
export {};

@ -53,10 +53,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
};
Object.defineProperty(exports, "__esModule", { value: true });
// @TODO:
// - PIC
// - warn on opcode non-function iff parameters
// - warn return/revert non-empty, comment ; !assert(+1 @extra)
// - $$
// - In JS add config (positionIndependent)
// - When checking name collisions, verify no collision in javascript
var path_1 = require("path");
@ -174,9 +171,8 @@ var Node = /** @class */ (function () {
throw new Error("cannot instantiate class");
}
logger.checkAbstract(_newTarget, Node);
ethers_1.ethers.utils.defineReadOnly(this, "location", location);
ethers_1.ethers.utils.defineReadOnly(this, "location", Object.freeze(location));
ethers_1.ethers.utils.defineReadOnly(this, "tag", "node-" + nextTag++ + "-" + this.constructor.name);
ethers_1.ethers.utils.defineReadOnly(this, "warnings", []);
for (var key in options) {
ethers_1.ethers.utils.defineReadOnly(this, key, options[key]);
}
@ -198,6 +194,9 @@ var Node = /** @class */ (function () {
};
Node.prototype.visit = function (visit) {
visit(this);
this.children().forEach(function (child) {
child.visit(visit);
});
};
Node.from = function (options) {
var Factories = {
@ -210,6 +209,7 @@ var Node = /** @class */ (function () {
length: LinkNode,
offset: LinkNode,
opcode: OpcodeNode,
pop: PopNode,
scope: ScopeNode,
};
var factory = Factories[options.type];
@ -221,14 +221,6 @@ var Node = /** @class */ (function () {
return Node;
}());
exports.Node = Node;
/*
export abstract class CodeNode extends Node {
constructor(guard: any, location: Location, options: { [ key: string ]: any }) {
logger.checkAbstract(new.target, CodeNode);
super(guard, location, options);
}
}
*/
var ValueNode = /** @class */ (function (_super) {
__extends(ValueNode, _super);
function ValueNode(guard, location, options) {
@ -288,6 +280,27 @@ var LiteralNode = /** @class */ (function (_super) {
return LiteralNode;
}(ValueNode));
exports.LiteralNode = LiteralNode;
var PopNode = /** @class */ (function (_super) {
__extends(PopNode, _super);
function PopNode(guard, location, index) {
return _super.call(this, guard, location, { index: index }) || this;
}
Object.defineProperty(PopNode.prototype, "placeholder", {
get: function () {
if (this.index === 0) {
return "$$";
}
return "$" + String(this.index);
},
enumerable: true,
configurable: true
});
PopNode.from = function (options) {
return new PopNode(Guard, options.loc, options.index);
};
return PopNode;
}(ValueNode));
exports.PopNode = PopNode;
var LinkNode = /** @class */ (function (_super) {
__extends(LinkNode, _super);
function LinkNode(guard, location, type, label) {
@ -295,32 +308,67 @@ var LinkNode = /** @class */ (function (_super) {
}
LinkNode.prototype.assemble = function (assembler, visit) {
return __awaiter(this, void 0, void 0, function () {
var value, target, result;
var value, isOffset, target, result, here, opcodes, literal, w;
return __generator(this, function (_a) {
assembler.start(this);
value = null;
isOffset = false;
target = assembler.getTarget(this.label);
if (target instanceof LabelNode) {
if (this.type === "offset") {
//value = assembler.getOffset(this.label);
value = (assembler.getLinkValue(target, this));
isOffset = true;
}
}
else {
result = (assembler.getLinkValue(target, this));
if (this.type === "offset") {
//value = assembler.getOffset(this.label);
value = result.offset;
isOffset = true;
}
else if (this.type === "length") {
//value = assembler.getLength(this.label);
value = result.length;
}
}
if (value == null) {
throw new Error("labels can only be targetted as offsets");
}
if (isOffset && assembler.positionIndependentCode) {
here = assembler.getOffset(this, this);
opcodes = [];
if (here > value) {
literal = "0x";
for (w = 1; w <= 5; w++) {
if (w > 4) {
throw new Error("jump too large!");
}
literal = pushLiteral(here - value + w);
if (ethers_1.ethers.utils.hexDataLength(literal) <= w) {
literal = ethers_1.ethers.utils.hexZeroPad(literal, w);
break;
}
}
opcodes.push(literal);
opcodes.push(opcodes_1.Opcode.from("PC"));
opcodes.push(opcodes_1.Opcode.from("SUB"));
// This also works, in case the above literal thing doesn't work out...
//opcodes.push(Opcode.from("PC"));
//opcodes.push(pushLiteral(-delta));
//opcodes.push(Opcode.from("SWAP1"));
//opcodes.push(Opcode.from("SUB"));
}
else {
// Jump forwards; this is easy to calculate since we can
// do PC firat.
opcodes.push(opcodes_1.Opcode.from("PC"));
opcodes.push(pushLiteral(value - here));
opcodes.push(opcodes_1.Opcode.from("ADD"));
}
visit(this, hexConcat(opcodes));
}
else {
visit(this, pushLiteral(value));
}
assembler.end(this);
return [2 /*return*/];
});
@ -335,12 +383,8 @@ var LinkNode = /** @class */ (function (_super) {
exports.LinkNode = LinkNode;
var OpcodeNode = /** @class */ (function (_super) {
__extends(OpcodeNode, _super);
function OpcodeNode(guard, location, opcode, operands) {
var _this = _super.call(this, guard, location, { opcode: opcode, operands: operands }) || this;
if (opcode.isPush()) {
_this.warnings.push("the PUSH opcode modifies program flow - use literals instead");
}
return _this;
function OpcodeNode(guard, location, opcode, operands, instructional) {
return _super.call(this, guard, location, { instructional: instructional, opcode: opcode, operands: operands }) || this;
}
OpcodeNode.prototype.assemble = function (assembler, visit) {
return __awaiter(this, void 0, void 0, function () {
@ -386,23 +430,14 @@ var OpcodeNode = /** @class */ (function (_super) {
if (!opcode) {
throw new Error("unknown opcode: " + options.mnemonic);
}
// Using the function syntax will check the operand count
if (!options.bare) {
if (opcode.mnemonic === "POP" && options.operands.length === 0) {
// This is ok... Pop has a delta of 0, but without operands
}
else if (options.operands.length !== opcode.delta) {
throw new Error("opcode " + opcode.mnemonic + " expects " + opcode.delta + " operands");
}
}
var operands = Object.freeze(options.operands.map(function (o) {
var operand = Node.from(o);
if (!(operand instanceof ValueNode)) {
throw new Error("invalid operand");
throw new Error("bad grammar?!");
}
return operand;
}));
return new OpcodeNode(Guard, options.loc, opcode, operands);
return new OpcodeNode(Guard, options.loc, opcode, operands, !!options.bare);
};
return OpcodeNode;
}(ValueNode));
@ -469,7 +504,7 @@ var DataNode = /** @class */ (function (_super) {
i_1++;
return [3 /*break*/, 1];
case 4:
bytecode = ethers_1.ethers.utils.arrayify(assembler.getPendingBytecode(this));
bytecode = ethers_1.ethers.utils.arrayify(assembler.getBytecode(this));
i = 0;
while (i < bytecode.length) {
opcode = opcodes_1.Opcode.from(bytecode[i++]);
@ -491,12 +526,6 @@ var DataNode = /** @class */ (function (_super) {
DataNode.prototype.children = function () {
return this.data;
};
DataNode.prototype.visit = function (visit) {
visit(this);
for (var i = 0; i < this.data.length; i++) {
this.data[i].visit(visit);
}
};
DataNode.from = function (options) {
if (options.type !== "data") {
throw new Error("expected data type");
@ -610,12 +639,6 @@ var ScopeNode = /** @class */ (function (_super) {
ScopeNode.prototype.children = function () {
return this.statements;
};
ScopeNode.prototype.visit = function (visit) {
visit(this);
for (var i = 0; i < this.statements.length; i++) {
this.statements[i].visit(visit);
}
};
ScopeNode.from = function (options) {
if (options.type !== "scope") {
throw new Error("expected scope type");
@ -625,57 +648,6 @@ var ScopeNode = /** @class */ (function (_super) {
return ScopeNode;
}(LabelledNode));
exports.ScopeNode = ScopeNode;
function parse(code) {
// Since jison allows \n, \r or \r\n line endings, we need some
// twekaing to get the correct position
var lines = [];
var offset = 0;
code.split(/(\r\n?|\n)/g).forEach(function (clump, index) {
if (index % 2) {
lines[lines.length - 1].line += clump;
}
else {
lines.push({ line: clump, offset: offset });
}
offset += clump.length;
});
// Add a mock-EOF to the end of the file so we don't out-of-bounds
// on the last character
if (lines.length) {
lines[lines.length - 1].line += "\n";
}
// Givens a line (1 offset) and column (0 offset) return the byte offset
var getOffset = function (line, column) {
var info = lines[line - 1];
if (!info || column >= info.line.length) {
throw new Error("out of range");
}
return info.offset + column;
};
// We use this in the _parser to convert locations to source
_parser_1.parser.yy._ethersLocation = function (loc) {
// The _ scope should call with null to get the full source
if (loc == null) {
return Object.freeze({
offset: 0,
length: code.length,
source: code
});
}
var offset = getOffset(loc.first_line, loc.first_column);
var end = getOffset(loc.last_line, loc.last_column);
return Object.freeze({
offset: offset,
length: (end - offset),
source: code.substring(offset, end)
});
};
var result = Node.from(_parser_1.parse(code));
// Nuke the source code lookup callback
_parser_1.parser.yy._ethersLocation = null;
return result;
}
exports.parse = parse;
function disassemble(bytecode) {
var ops = [];
var offsets = {};
@ -741,14 +713,10 @@ function formatBytecode(bytecode) {
return lines.join("\n");
}
exports.formatBytecode = formatBytecode;
// @TODO: Rename to Assembler?
var Assembler = /** @class */ (function () {
function Assembler(root, options) {
ethers_1.ethers.utils.defineReadOnly(this, "positionIndependentCode", !!options.positionIndependentCode);
ethers_1.ethers.utils.defineReadOnly(this, "retry", ((options.retry != null) ? options.retry : 512));
ethers_1.ethers.utils.defineReadOnly(this, "filename", path_1.resolve(options.filename || "./contract.asm"));
ethers_1.ethers.utils.defineReadOnly(this, "defines", Object.freeze(options.defines || {}));
function Assembler(root, positionIndependentCode) {
ethers_1.ethers.utils.defineReadOnly(this, "root", root);
ethers_1.ethers.utils.defineReadOnly(this, "positionIndependentCode", !!positionIndependentCode);
var nodes = {};
var labels = {};
var parents = {};
@ -757,8 +725,7 @@ var Assembler = /** @class */ (function () {
nodes[node.tag] = {
node: node,
offset: 0x0,
bytecode: "0x",
pending: "0x"
bytecode: "0x"
};
if (node instanceof LabelledNode) {
// Check for duplicate labels
@ -784,58 +751,14 @@ var Assembler = /** @class */ (function () {
ethers_1.ethers.utils.defineReadOnly(this, "labels", Object.freeze(labels));
ethers_1.ethers.utils.defineReadOnly(this, "nodes", Object.freeze(nodes));
ethers_1.ethers.utils.defineReadOnly(this, "_parents", Object.freeze(parents));
ethers_1.ethers.utils.defineReadOnly(this, "_stack", []);
this.reset();
}
Object.defineProperty(Assembler.prototype, "changed", {
get: function () {
return this._changed;
},
enumerable: true,
configurable: true
});
// Link operations
Assembler.prototype.getTarget = function (name) {
return this.labels[name];
};
// Reset the assmebler for another run with updated values
Assembler.prototype.reset = function () {
var _this = this;
this._changed = false;
for (var tag in this.nodes) {
delete this.nodes[tag].object;
}
this._script = new Script(this.filename, function (name, context) {
return _this.get(name, context);
});
Assembler.prototype.getTarget = function (label) {
return this.labels[label];
};
// Evaluate script in the context of a {{! }} or {{= }}
Assembler.prototype.evaluate = function (script, source) {
return this._script.evaluate(script, source);
};
Assembler.prototype.start = function (node) {
this._stack.push(node);
var info = this.nodes[node.tag];
info.pending = "0x";
};
Assembler.prototype.end = function (node) {
if (this._stack.pop() !== node) {
throw new Error("missing push/pop pair");
}
var info = this.nodes[node.tag];
if (info.pending !== info.bytecode) {
this._didChange();
}
info.bytecode = info.pending;
};
Assembler.prototype.getPendingBytecode = function (node) {
return this.nodes[node.tag].pending;
};
Assembler.prototype._appendBytecode = function (bytecode) {
var _this = this;
this._stack.forEach(function (node) {
var info = _this.nodes[node.tag];
info.pending = hexConcat([info.pending, bytecode]);
});
return Promise.resolve(new Uint8Array(0));
};
Assembler.prototype.getAncestor = function (node, cls) {
node = this._parents[node.tag];
@ -847,6 +770,23 @@ var Assembler = /** @class */ (function () {
}
return null;
};
Assembler.prototype.getOffset = function (node, source) {
var offset = this.nodes[node.tag].offset;
if (source == null) {
return offset;
}
var sourceScope = ((source instanceof ScopeNode) ? source : this.getAncestor(source, ScopeNode));
return offset - this.nodes[sourceScope.tag].offset;
};
Assembler.prototype.setOffset = function (node, offset) {
this.nodes[node.tag].offset = offset;
};
Assembler.prototype.getBytecode = function (node) {
return this.nodes[node.tag].bytecode;
};
Assembler.prototype.setBytecode = function (node, bytecode) {
this.nodes[node.tag].bytecode = bytecode;
};
Assembler.prototype.getLinkValue = function (target, source) {
var sourceScope = ((source instanceof ScopeNode) ? source : this.getAncestor(source, ScopeNode));
var targetScope = ((target instanceof ScopeNode) ? target : this.getAncestor(target, ScopeNode));
@ -857,13 +797,7 @@ var Assembler = /** @class */ (function () {
throw new Error("cannot access " + target.name + " from " + source.tag);
}
// Return the offset relative to its scope
var offset = this.nodes[target.tag].offset - this.nodes[targetScope.tag].offset;
// Offsets are wrong; but we should finish this run and then try again
if (offset < 0) {
offset = 0;
this._didChange();
}
return offset;
return this.nodes[target.tag].offset - this.nodes[targetScope.tag].offset;
}
var info = this.nodes[target.tag];
// Return the offset is relative to its scope
@ -892,15 +826,224 @@ var Assembler = /** @class */ (function () {
// been marked as invalid, in which case accessing it will fail
if (safeOffset) {
bytes.offset = info.offset - this.nodes[sourceScope.tag].offset;
// Offsets are wqrong; but we should finish this run and then try again
if (bytes.offset < 0) {
bytes.offset = 0;
}
return bytes;
};
Assembler.prototype.start = function (node) { };
Assembler.prototype.end = function (node) { };
return Assembler;
}());
var SemanticErrorSeverity;
(function (SemanticErrorSeverity) {
SemanticErrorSeverity["error"] = "error";
SemanticErrorSeverity["warning"] = "warning";
})(SemanticErrorSeverity = exports.SemanticErrorSeverity || (exports.SemanticErrorSeverity = {}));
;
// This Assembler is designed to only check for errors and warnings
// Warnings
// - Bare PUSH opcodes
// - Instructional opcode that has parameters
// Errors
// - Using a $$ outside of RPN
// - Using a $$ when it is not adjacent to the stack
// - The operand count does not match the opcode
// - An opcode is used as an operand but does not return a value
var SemanticChecker = /** @class */ (function (_super) {
__extends(SemanticChecker, _super);
function SemanticChecker() {
return _super !== null && _super.apply(this, arguments) || this;
}
SemanticChecker.prototype.check = function () {
var _this = this;
var errors = [];
this.root.visit(function (node) {
if (node instanceof OpcodeNode) {
var opcode = node.opcode;
if (node.instructional) {
if (opcode.delta) {
errors.push({
message: opcode.mnemonic + " used as instructional",
severity: SemanticErrorSeverity.warning,
node: node
});
}
}
else {
if (opcode.mnemonic === "POP") {
if (node.operands.length !== 0) {
errors.push({
message: "POP expects 0 operands",
severity: SemanticErrorSeverity.error,
node: node
});
}
}
else if (node.operands.length !== opcode.delta) {
errors.push({
message: opcode.mnemonic + " expects " + opcode.delta + " operands",
severity: SemanticErrorSeverity.error,
node: node
});
}
}
if (opcode.isPush()) {
// A stray PUSH operation will gobble up the following code
// bytes which is bad. But this may be a disassembled program
// and that PUSH may actually be just some data (which is safe)
errors.push({
message: "PUSH opcode modifies program flow - use literals instead",
severity: SemanticErrorSeverity.warning,
node: node
});
}
else if (!node.location.statement && opcode.alpha !== 1) {
// If an opcode does not push anything on the stack, it
// cannot be used as an operand
errors.push({
message: node.opcode.mnemonic + " cannot be an operand",
severity: SemanticErrorSeverity.error,
node: node
});
}
}
if (node.location.statement) {
if (node instanceof PopNode) {
// $$ by istelf is useless and is intended to be an operand
errors.push({
message: "$$ must be an operand",
severity: SemanticErrorSeverity.error,
node: node
});
}
else {
var scope_1 = _this.getAncestor(node, ScopeNode);
// Make sure any $$ is stack adjacent (within this scope)
var ordered_1 = [];
node.visit(function (node) {
if (scope_1 !== _this.getAncestor(node, ScopeNode)) {
return;
}
ordered_1.push(node);
});
// Allow any number of stack adjacent $$
var foundZero = null;
var lastIndex = 0;
while (ordered_1.length && ordered_1[0] instanceof PopNode) {
var popNode = (ordered_1.shift());
var index = popNode.index;
if (index === 0) {
foundZero = popNode;
}
else if (index !== lastIndex + 1) {
errors.push({
message: "out-of-order stack placeholder " + popNode.placeholder + "; expected $$" + (lastIndex + 1),
severity: SemanticErrorSeverity.error,
node: popNode
});
while (ordered_1.length && ordered_1[0] instanceof PopNode) {
ordered_1.shift();
}
break;
}
else {
lastIndex = index;
}
}
if (foundZero && lastIndex > 0) {
errors.push({
message: "cannot mix $$ and $1 stack placeholder",
severity: SemanticErrorSeverity.error,
node: foundZero
});
}
// If there are still any buried, we have a problem
var pops = ordered_1.filter(function (n) { return (n instanceof PopNode); });
if (pops.length) {
errors.push({
message: "stack placeholder " + (pops[0]).placeholder + " must be stack adjacent",
severity: SemanticErrorSeverity.error,
node: pops[0]
});
}
}
}
});
return errors;
};
return SemanticChecker;
}(Assembler));
var CodeGenerationAssembler = /** @class */ (function (_super) {
__extends(CodeGenerationAssembler, _super);
function CodeGenerationAssembler(root, options) {
var _this = _super.call(this, root, !!options.positionIndependentCode) || this;
ethers_1.ethers.utils.defineReadOnly(_this, "retry", ((options.retry != null) ? options.retry : 512));
ethers_1.ethers.utils.defineReadOnly(_this, "filename", path_1.resolve(options.filename || "./contract.asm"));
ethers_1.ethers.utils.defineReadOnly(_this, "defines", Object.freeze(options.defines || {}));
ethers_1.ethers.utils.defineReadOnly(_this, "_stack", []);
_this.reset();
return _this;
}
CodeGenerationAssembler.prototype._didChange = function () {
this._changed = true;
};
Object.defineProperty(CodeGenerationAssembler.prototype, "changed", {
get: function () {
return this._changed;
},
enumerable: true,
configurable: true
});
// Reset the assmebler for another run with updated values
CodeGenerationAssembler.prototype.reset = function () {
var _this = this;
this._changed = false;
this._oldBytecode = {};
this._objectCache = {};
this._script = new Script(this.filename, function (name, context) {
return _this.get(name, context);
});
};
CodeGenerationAssembler.prototype.evaluate = function (script, source) {
return this._script.evaluate(script, source);
};
CodeGenerationAssembler.prototype.getLinkValue = function (target, source) {
// Since we are iteratively generating code, offsets and lengths
// may not be stable at any given point in time, so if an offset
// is negative the code is obviously wrong, however we set it to
// 0 so we can proceed with generation to fill in as many blanks
// as possible; then we will try assembling again
var result = _super.prototype.getLinkValue.call(this, target, source);
if (typeof (result) === "number") {
if (result < 0) {
this._didChange();
return 0;
}
return result;
}
if (result.offset < 0) {
result.offset = 0;
this._didChange();
}
}
return Object.freeze(bytes);
return result;
};
Assembler.prototype.get = function (name, source) {
CodeGenerationAssembler.prototype.start = function (node) {
this._stack.push(node);
this._oldBytecode[node.tag] = this.getBytecode(node);
this.setBytecode(node, "0x");
};
CodeGenerationAssembler.prototype.end = function (node) {
if (this._stack.pop() !== node) {
throw new Error("missing push/pop pair");
}
if (this._oldBytecode[node.tag] !== this.getBytecode(node)) {
this._didChange();
}
};
// This is used by evaluate to access properties in JavaScript
// - "defines" allow meta-programming values to be used
// - jump destinations are available as numbers
// - bytecode and data are available as an immuatble DataSource
CodeGenerationAssembler.prototype.get = function (name, source) {
if (name === "defines") {
return this.defines;
}
@ -908,55 +1051,67 @@ var Assembler = /** @class */ (function () {
if (!node) {
return undefined;
}
var info = this.nodes[node.tag];
if (info.object == null) {
info.object = this.getLinkValue(node, source);
// We cache objects when they are generated so all nodes
// receive consistent data; if there is a change we will
// run the entire assembly process again with the updated
// values
if (this._objectCache[node.tag] == null) {
this._objectCache[node.tag] = Object.freeze(this.getLinkValue(node, source));
}
return info.object;
return this._objectCache[node.tag];
};
Assembler.prototype._didChange = function () {
this._changed = true;
};
Assembler.prototype._assemble = function () {
CodeGenerationAssembler.prototype._assemble = function () {
return __awaiter(this, void 0, void 0, function () {
var offset, bytecodes;
var offset;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
offset = 0;
bytecodes = [];
return [4 /*yield*/, this.root.assemble(this, function (node, bytecode) {
var state = _this.nodes[node.tag];
// Things have moved; we will need to try again
if (state.offset !== offset) {
state.offset = offset;
if (_this.getOffset(node) !== offset) {
_this.setOffset(node, offset);
_this._didChange();
}
_this._appendBytecode(bytecode);
bytecodes.push(bytecode);
// The bytecode has changed; we will need to try again
//if (state.bytecode !== bytecode) {
// state.bytecode = bytecode;
// this._didChange();
//}
_this._stack.forEach(function (node) {
_this.setBytecode(node, hexConcat([
_this.getBytecode(node),
bytecode
]));
});
offset += ethers_1.ethers.utils.hexDataLength(bytecode);
})];
case 1:
_a.sent();
return [2 /*return*/, hexConcat(bytecodes)];
return [2 /*return*/];
}
});
});
};
Assembler.prototype.assemble = function () {
CodeGenerationAssembler.prototype.assemble = function (label) {
return __awaiter(this, void 0, void 0, function () {
var bytecode, i, adjusted;
var target, i;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this._assemble()];
case 0:
if (label == null) {
label = "_";
}
target = this.getTarget(label);
if (!target) {
logger.throwArgumentError("unknown labelled target: " + label, "label", label);
}
else if (!(target instanceof ScopeNode || target instanceof DataNode)) {
logger.throwArgumentError("cannot assemble a bodyless label: " + label, "label", label);
}
// Continue re-evaluating the bytecode until a stable set of
// offsets, length and values are reached.
return [4 /*yield*/, this._assemble()];
case 1:
bytecode = _a.sent();
// Continue re-evaluating the bytecode until a stable set of
// offsets, length and values are reached.
_a.sent();
i = 0;
_a.label = 2;
case 2:
@ -965,37 +1120,101 @@ var Assembler = /** @class */ (function () {
this.reset();
return [4 /*yield*/, this._assemble()];
case 3:
adjusted = _a.sent();
_a.sent();
// Generated bytecode is stable!! :)
if (!this.changed) {
console.log("Assembled in " + i + " attempts");
return [2 /*return*/, bytecode];
// This should not happen; something is wrong with the grammar
// or missing enter/exit call in assemble
if (this._stack.length !== 0) {
throw new Error("Bad AST! Bad grammar?!");
}
//console.log(`Assembled in ${ i } attempts`);
return [2 /*return*/, this.getBytecode(target)];
;
}
// Try again...
bytecode = adjusted;
_a.label = 4;
case 4:
i++;
return [3 /*break*/, 2];
case 5:
// This should not happen; something is wrong with the grammar
// or missing enter/exit call in assemble
if (this._stack.length !== 0) {
throw new Error("bad AST");
}
return [2 /*return*/, logger.throwError("unable to assemble; " + this.retry + " attempts failed to generate stable bytecode", ethers_1.ethers.utils.Logger.errors.UNKNOWN_ERROR, {})];
case 5: return [2 /*return*/, logger.throwError("unable to assemble; " + this.retry + " attempts failed to generate stable bytecode", ethers_1.ethers.utils.Logger.errors.UNKNOWN_ERROR, {})];
}
});
});
};
return Assembler;
}());
return CodeGenerationAssembler;
}(Assembler));
function parse(code, options) {
if (options == null) {
options = {};
}
// Since jison allows \n, \r or \r\n line endings, we need some
// twekaing to get the correct position
var lines = [];
var offset = 0;
code.split(/(\r\n?|\n)/g).forEach(function (clump, index) {
if (index % 2) {
lines[lines.length - 1].line += clump;
}
else {
lines.push({ line: clump, offset: offset });
}
offset += clump.length;
});
// Add a mock-EOF to the end of the file so we don't out-of-bounds
// on the last character
if (lines.length) {
lines[lines.length - 1].line += "\n";
}
// Givens a line (1 offset) and column (0 offset) return the byte offset
var getOffset = function (line, column) {
var info = lines[line - 1];
if (!info || column >= info.line.length) {
throw new Error("out of range");
}
return info.offset + column;
};
// We use this in the _parser to convert locations to source
_parser_1.parser.yy._ethersLocation = function (loc) {
// The _ scope should call with null to get the full source
if (loc == null) {
return {
offset: 0,
line: 0,
length: code.length,
source: code,
statement: true
};
}
var offset = getOffset(loc.first_line, loc.first_column);
var end = getOffset(loc.last_line, loc.last_column);
return {
offset: offset,
line: loc.first_line - 1,
length: (end - offset),
source: code.substring(offset, end),
statement: (!!loc.statement)
};
};
var result = Node.from(_parser_1.parse(code));
// Nuke the source code lookup callback
_parser_1.parser.yy._ethersLocation = null;
// Semantic Checks
var checker = new SemanticChecker(result);
var errors = checker.check();
if (errors.filter(function (e) { return (e.severity === SemanticErrorSeverity.error); }).length || (errors.length && !options.ignoreWarnings)) {
var error = new Error("semantic errors during parsing");
error.errors = errors;
throw error;
}
return result;
}
exports.parse = parse;
function assemble(ast, options) {
return __awaiter(this, void 0, void 0, function () {
var assembler;
return __generator(this, function (_a) {
assembler = new Assembler(ast, options || {});
return [2 /*return*/, assembler.assemble()];
assembler = new CodeGenerationAssembler(ast, options || {});
return [2 /*return*/, assembler.assemble(options.target || "_")];
});
});
}

@ -1,4 +1,4 @@
import { assemble, DataNode, disassemble, EvaluationNode, ExecutionNode, formatBytecode, LabelNode, LabelledNode, LinkNode, LiteralNode, Node, OpcodeNode, parse, ScopeNode, ValueNode } from "./assembler";
import { assemble, DataNode, disassemble, EvaluationNode, ExecutionNode, formatBytecode, LabelNode, LabelledNode, LinkNode, LiteralNode, Node, OpcodeNode, parse, PopNode, ScopeNode, ValueNode } from "./assembler";
import { Opcode } from "./opcodes";
import { AssemblerOptions, AssembleVisitFunc, Bytecode, Location, Operation, VisitFunc } from "./assembler";
export { Opcode, assemble, disassemble, formatBytecode, parse, DataNode, EvaluationNode, ExecutionNode, LabelNode, LabelledNode, LinkNode, LiteralNode, Node, OpcodeNode, ScopeNode, ValueNode, AssemblerOptions, AssembleVisitFunc, Bytecode, Location, Operation, VisitFunc, };
import { AssemblerOptions, AssembleVisitFunc, Bytecode, Location, Operation, ParserOptions, SemanticError, SemanticErrorSeverity, VisitFunc } from "./assembler";
export { Opcode, assemble, disassemble, formatBytecode, parse, DataNode, EvaluationNode, ExecutionNode, LabelNode, LabelledNode, LinkNode, LiteralNode, Node, OpcodeNode, PopNode, ScopeNode, ValueNode, AssemblerOptions, AssembleVisitFunc, Bytecode, Location, Operation, SemanticError, SemanticErrorSeverity, ParserOptions, VisitFunc, };

@ -14,7 +14,10 @@ exports.LiteralNode = assembler_1.LiteralNode;
exports.Node = assembler_1.Node;
exports.OpcodeNode = assembler_1.OpcodeNode;
exports.parse = assembler_1.parse;
exports.PopNode = assembler_1.PopNode;
exports.ScopeNode = assembler_1.ScopeNode;
exports.ValueNode = assembler_1.ValueNode;
var opcodes_1 = require("./opcodes");
exports.Opcode = opcodes_1.Opcode;
var assembler_2 = require("./assembler");
exports.SemanticErrorSeverity = assembler_2.SemanticErrorSeverity;

@ -1,3 +1,8 @@
export declare enum OpcodeMemoryAccess {
write = "write",
read = "read",
full = "full"
}
export declare class Opcode {
readonly value: number;
readonly mnemonic: string;
@ -8,7 +13,7 @@ export declare class Opcode {
isJump(): boolean;
isValidJumpDest(): boolean;
isPush(): number;
isMemory(readOrWrite?: boolean): boolean;
isMemoryAccess(readOrWrite?: boolean): OpcodeMemoryAccess;
isStatic(): boolean;
static from(valueOrMnemonic: number | string): Opcode;
}

@ -9,6 +9,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
// EXTCODEHASH
// See: https://eips.ethereum.org/EIPS/eip-1052
var ethers_1 = require("ethers");
var OpcodeMemoryAccess;
(function (OpcodeMemoryAccess) {
OpcodeMemoryAccess["write"] = "write";
OpcodeMemoryAccess["read"] = "read";
OpcodeMemoryAccess["full"] = "full";
})(OpcodeMemoryAccess = exports.OpcodeMemoryAccess || (exports.OpcodeMemoryAccess = {}));
;
var Opcode = /** @class */ (function () {
function Opcode(mnemonic, value, delta, alpha, doc) {
ethers_1.ethers.utils.defineReadOnly(this, "mnemonic", mnemonic);
@ -32,12 +39,19 @@ var Opcode = /** @class */ (function () {
return 0;
};
// Returns true if this operation writes to memory contents (or if readOrWrite, reads memory)
Opcode.prototype.isMemory = function (readOrWrite) {
throw new Error("@TODO: return true if modifies memory");
// Unknown opcodes return null
Opcode.prototype.isMemoryAccess = function (readOrWrite) {
switch ((_Opcodes[this.mnemonic.toLowerCase()] || { memory: null }).memory) {
case "read": return OpcodeMemoryAccess.read;
case "write": return OpcodeMemoryAccess.write;
case "full": return OpcodeMemoryAccess.full;
}
return null;
};
// Returns true if this opcode does not affect state
// Unknown opcodes return false
Opcode.prototype.isStatic = function () {
throw new Error("@TODO: return true if certain non-state-changing");
return !(_Opcodes[this.mnemonic.toLowerCase()] || { nonStatic: true }).nonStatic;
};
Opcode.from = function (valueOrMnemonic) {
if (typeof (valueOrMnemonic) === "string") {
@ -78,7 +92,7 @@ var _Opcodes = {
shr: { value: 0x1c, delta: 2, alpha: 1, doc: "v = shr(shiftBits, value)" },
sar: { value: 0x1d, delta: 2, alpha: 1, doc: "v = sar(shiftBits, value)" },
// SHA3
sha3: { value: 0x20, delta: 2, alpha: 1, doc: "v = sha3(offset, length)" },
sha3: { value: 0x20, delta: 2, alpha: 1, doc: "v = sha3(offset, length)", memory: "read" },
// Environmental Information
address: { value: 0x30, delta: 0, alpha: 1, doc: "myAddr = address" },
balance: { value: 0x31, delta: 1, alpha: 1, doc: "wei = balance(address)" },
@ -87,14 +101,14 @@ var _Opcodes = {
callvalue: { value: 0x34, delta: 0, alpha: 1, doc: "msgValue = callvalue" },
calldataload: { value: 0x35, delta: 1, alpha: 1, doc: "calldataWordValue = calldataload(byteOffet)" },
calldatasize: { value: 0x36, delta: 0, alpha: 1, doc: "calldataLength = calldatasize" },
calldatacopy: { value: 0x37, delta: 3, alpha: 0, doc: "calldatacopy(dstMemoryIndex, dataIndex, length)" },
calldatacopy: { value: 0x37, delta: 3, alpha: 0, doc: "calldatacopy(dstMemoryIndex, dataIndex, length)", memory: "write" },
codesize: { value: 0x38, delta: 0, alpha: 1, doc: "myCodeLength = codesize" },
codecopy: { value: 0x39, delta: 3, alpha: 0, doc: "codecopy(dstMemoryIndex, codeIndex, length)" },
codecopy: { value: 0x39, delta: 3, alpha: 0, doc: "codecopy(dstMemoryIndex, codeIndex, length)", memory: "write" },
gasprice: { value: 0x3a, delta: 0, alpha: 1, doc: "txGasPrice = gasprice" },
extcodesize: { value: 0x3b, delta: 1, alpha: 1, doc: "otherCodeLength = extcodesize(address)" },
extcodecopy: { value: 0x3c, delta: 4, alpha: 0, doc: "extcodecopy(address, dstMemoryIndex, extcodeIndex, length)" },
extcodecopy: { value: 0x3c, delta: 4, alpha: 0, doc: "extcodecopy(address, dstMemoryIndex, extcodeIndex, length)", memory: "write" },
returndatasize: { value: 0x3d, delta: 0, alpha: 1, doc: "v = returndatasize" },
returndatacopy: { value: 0x3e, delta: 3, alpha: 0, doc: "returndatacopy(dstMemoryOffset, returndataIndex, length)" },
returndatacopy: { value: 0x3e, delta: 3, alpha: 0, doc: "returndatacopy(dstMemoryOffset, returndataIndex, length)", memory: "write" },
extcodehash: { value: 0x3f, delta: 1, alpha: 1, doc: "hash = extcodehash(address)" },
// Block Information
blockhash: { value: 0x40, delta: 1, alpha: 1, doc: "hash = blockhash(blockNumber)" },
@ -105,11 +119,11 @@ var _Opcodes = {
gaslimit: { value: 0x45, delta: 0, alpha: 1, doc: "gas = gaslimit" },
// Stack, Memory, Storage and Flow Operations
pop: { value: 0x50, delta: 1, alpha: 0, doc: "stackTopValue = pop" },
mload: { value: 0x51, delta: 1, alpha: 1, doc: "memoryWordValue = mload(memoryByteIndex)" },
mstore: { value: 0x52, delta: 2, alpha: 0, doc: "mstore(memoryByteIndex, valueOut)" },
mstore8: { value: 0x53, delta: 2, alpha: 0, doc: "mstore8(memoryByteIndex, valueOut [ & 0xff ])" },
mload: { value: 0x51, delta: 1, alpha: 1, doc: "memoryWordValue = mload(memoryByteIndex)", memory: "read" },
mstore: { value: 0x52, delta: 2, alpha: 0, doc: "mstore(memoryByteIndex, valueOut)", memory: "write" },
mstore8: { value: 0x53, delta: 2, alpha: 0, doc: "mstore8(memoryByteIndex, valueOut [ & 0xff ])", memory: "write" },
sload: { value: 0x54, delta: 1, alpha: 1, doc: "storageWordValue = sload(storageWordIndex)" },
sstore: { value: 0x55, delta: 2, alpha: 0, doc: "sstore(storageWordIndex, valueOut)" },
sstore: { value: 0x55, delta: 2, alpha: 0, doc: "sstore(storageWordIndex, valueOut)", nonStatic: true },
jump: { value: 0x56, delta: 1, alpha: 0, doc: "jump(target)" },
jumpi: { value: 0x57, delta: 2, alpha: 0, doc: "jumpi(target, notZero)" },
pc: { value: 0x58, delta: 0, alpha: 1, doc: "programCounter = pc" },
@ -184,22 +198,22 @@ var _Opcodes = {
swap15: { value: 0x9e, delta: 0, alpha: 0 },
swap16: { value: 0x9f, delta: 0, alpha: 0 },
// Loggin Operations
log0: { value: 0xa0, delta: 2, alpha: 0 },
log1: { value: 0xa1, delta: 3, alpha: 0 },
log2: { value: 0xa2, delta: 4, alpha: 0 },
log3: { value: 0xa3, delta: 5, alpha: 0 },
log4: { value: 0xa4, delta: 6, alpha: 0 },
log0: { value: 0xa0, delta: 2, alpha: 0, nonStatic: true, memory: "read" },
log1: { value: 0xa1, delta: 3, alpha: 0, nonStatic: true, memory: "read" },
log2: { value: 0xa2, delta: 4, alpha: 0, nonStatic: true, memory: "read" },
log3: { value: 0xa3, delta: 5, alpha: 0, nonStatic: true, memory: "read" },
log4: { value: 0xa4, delta: 6, alpha: 0, nonStatic: true, memory: "read" },
// System Operations
create: { value: 0xf0, delta: 3, alpha: 1, doc: "address = create(value, index, length)" },
call: { value: 0xf1, delta: 7, alpha: 1, doc: "v = call(gasLimit, address, value, inputIndex, inputLength, outputIndex, outputLength)" },
callcode: { value: 0xf2, delta: 7, alpha: 1, doc: "v = callcode(@TODO)" },
"return": { value: 0xf3, delta: 2, alpha: 0, doc: "return(index, length)" },
delegatecall: { value: 0xf4, delta: 6, alpha: 1, doc: "v = delegatecall(gasLimit, address, inputIndex, inputLength, outputIndex, outputLength)" },
create2: { value: 0xf5, delta: 4, alpha: 1, doc: "address = create2(value, index, length, salt)" },
staticcall: { value: 0xfa, delta: 6, alpha: 1, doc: "v = staticcall(gasLimit, address, inputIndex, inputLength, outputIndex, outputLength)" },
revert: { value: 0xfd, delta: 2, alpha: 0, doc: "revert(returnDataOffset, returnDataLength)" },
create: { value: 0xf0, delta: 3, alpha: 1, doc: "address = create(value, index, length)", nonStatic: true, memory: "read" },
call: { value: 0xf1, delta: 7, alpha: 1, doc: "v = call(gasLimit, address, value, inputIndex, inputLength, outputIndex, outputLength)", nonStatic: true, memory: "full" },
callcode: { value: 0xf2, delta: 7, alpha: 1, doc: "v = callcode(@TODO)", nonStatic: true, memory: "full" },
"return": { value: 0xf3, delta: 2, alpha: 0, doc: "return(index, length)", memory: "read" },
delegatecall: { value: 0xf4, delta: 6, alpha: 1, doc: "v = delegatecall(gasLimit, address, inputIndex, inputLength, outputIndex, outputLength)", nonStatic: true, memory: "full" },
create2: { value: 0xf5, delta: 4, alpha: 1, doc: "address = create2(value, index, length, salt)", nonStatic: true, memory: "read" },
staticcall: { value: 0xfa, delta: 6, alpha: 1, doc: "v = staticcall(gasLimit, address, inputIndex, inputLength, outputIndex, outputLength)", memory: "full" },
revert: { value: 0xfd, delta: 2, alpha: 0, doc: "revert(returnDataOffset, returnDataLength)", memory: "read" },
invalid: { value: 0xfe, delta: 0, alpha: 0, doc: "invalid" },
suicide: { value: 0xff, delta: 1, alpha: 0, doc: "suicide(targetAddress)" },
suicide: { value: 0xff, delta: 1, alpha: 0, doc: "suicide(targetAddress)", nonStatic: true },
};
var OpcodeMap = {};
var Opcodes = [];

@ -29,7 +29,7 @@
"generate": "node ./generate.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"tarballHash": "0xff54a87fe1756a7bc92d6d3b007dd65d85713495eabf6186a44f687098b67aa1",
"tarballHash": "0x6a57190906839e057c493a8ffaf2c5e57b4ac513d882bddc115884bc5092649b",
"types": "./lib/index.d.ts",
"version": "5.0.0-beta.148"
"version": "5.0.0-beta.149"
}

@ -1 +1 @@
export const version = "asm/5.0.0-beta.148";
export const version = "asm/5.0.0-beta.149";

@ -1 +1 @@
export declare const version = "basex/5.0.0-beta.131";
export declare const version = "basex/5.0.0-beta.132";

@ -1 +1 @@
export const version = "basex/5.0.0-beta.131";
export const version = "basex/5.0.0-beta.132";

@ -41,8 +41,10 @@ import { BytesLike } from "@ethersproject/bytes";
export declare class BaseX {
readonly alphabet: string;
readonly base: number;
private _alphabetMap;
private _leader;
_alphabetMap: {
[character: string]: number;
};
_leader: string;
constructor(alphabet: string);
encode(value: BytesLike): string;
decode(value: string): Uint8Array;

@ -1 +1 @@
export declare const version = "basex/5.0.0-beta.131";
export declare const version = "basex/5.0.0-beta.132";

@ -1,3 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = "basex/5.0.0-beta.131";
exports.version = "basex/5.0.0-beta.132";

@ -41,8 +41,10 @@ import { BytesLike } from "@ethersproject/bytes";
export declare class BaseX {
readonly alphabet: string;
readonly base: number;
private _alphabetMap;
private _leader;
_alphabetMap: {
[character: string]: number;
};
_leader: string;
constructor(alphabet: string);
encode(value: BytesLike): string;
decode(value: string): Uint8Array;

@ -24,7 +24,7 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"tarballHash": "0xb05390e395a38be994749f03651ccc3c7d744517c9437e0fe1d1a7679266a33f",
"tarballHash": "0x4f195cbe70a4657e86326d67c58dc6e244b4c816eafc232c5a8f82bb43d3dba3",
"types": "./lib/index.d.ts",
"version": "5.0.0-beta.131"
"version": "5.0.0-beta.132"
}

@ -1 +1 @@
export const version = "basex/5.0.0-beta.131";
export const version = "basex/5.0.0-beta.132";

@ -1 +1 @@
export declare const version = "cli/5.0.0-beta.150";
export declare const version = "cli/5.0.0-beta.151";

@ -1 +1 @@
export const version = "cli/5.0.0-beta.150";
export const version = "cli/5.0.0-beta.151";

@ -11,7 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
};
import fs from "fs";
import { resolve } from "path";
import { assemble, disassemble, formatBytecode, parse } from "@ethersproject/asm";
import { assemble, disassemble, formatBytecode, parse, SemanticErrorSeverity } from "@ethersproject/asm";
import { CLI, Plugin } from "../cli";
function repeat(text, length) {
if (text.length === 0) {
@ -37,14 +37,26 @@ class AssemblePlugin extends Plugin {
}
static getOptionHelp() {
return [
{
name: "--define KEY=VALUE",
help: "provide assembler defines"
},
{
name: "--disassemble",
help: "Disassemble input bytecode"
},
{
name: "--define KEY=VALUE",
help: "provide assembler defines"
}
name: "--ignore-warnings",
help: "Ignore warnings"
},
{
name: "--pic",
help: "generate position independent code"
},
{
name: "--target LABEL",
help: "output LABEL bytecode (default: _)"
},
];
}
prepareOptions(argParser) {
@ -64,6 +76,9 @@ class AssemblePlugin extends Plugin {
});
// We are disassembling...
this.disassemble = argParser.consumeFlag("disassemble");
this.ignoreWarnings = argParser.consumeFlag("ignore-warnings");
this.pic = argParser.consumeFlag("pic");
this.target = argParser.consumeOption("target");
});
}
prepareArgs(args) {
@ -126,11 +141,37 @@ class AssemblePlugin extends Plugin {
console.log(formatBytecode(disassemble(this.content)));
}
else {
console.log(yield assemble(parse(this.content), {
try {
const ast = parse(this.content, {
ignoreWarnings: !!this.ignoreWarnings
});
console.log(yield assemble(ast, {
defines: this.defines,
filename: this.filename,
positionIndependentCode: this.pic,
target: (this.target || "_")
}));
}
catch (error) {
if (error.errors) {
(error.errors).forEach((error) => {
if (error.severity === SemanticErrorSeverity.error) {
console.log(`Error: ${error.message} (line: ${error.node.location.line + 1})`);
}
else if (error.severity === SemanticErrorSeverity.warning) {
console.log(`Warning: ${error.message} (line: ${error.node.location.line + 1})`);
}
else {
console.log(error);
return;
}
});
}
else {
throw error;
}
}
}
});
}
}

@ -238,15 +238,6 @@ class AccountPlugin extends EnsPlugin {
}
});
}
prepareOptions(argParser) {
const _super = Object.create(null, {
prepareOptions: { get: () => super.prepareOptions }
});
return __awaiter(this, void 0, void 0, function* () {
yield _super.prepareOptions.call(this, argParser);
ethers.utils.defineReadOnly(this, "_wait", argParser.consumeFlag("wait"));
});
}
prepareArgs(args) {
const _super = Object.create(null, {
prepareArgs: { get: () => super.prepareArgs }

@ -12,12 +12,22 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
import fs from "fs";
import { dirname, resolve } from "path";
import REPL from "repl";
import util from "util";
import vm from "vm";
import { ethers } from "ethers";
import { parseExpression as babelParseExpression } from "@babel/parser";
import { CLI, dump, Plugin } from "../cli";
import { getPassword, getProgressBar } from "../prompt";
import { compile, customRequire } from "../solc";
function repeat(c, length) {
if (c.length === 0) {
throw new Error("too short");
}
let result = c;
while (result.length < length) {
result += result;
}
return result.substring(0, length);
}
function setupContext(path, context, plugin) {
context.provider = plugin.provider;
context.accounts = plugin.accounts;
@ -31,7 +41,6 @@ function setupContext(path, context, plugin) {
context.console = console;
}
if (!context.require) {
//context.require = _module.createRequireFromPath(path);
context.require = customRequire(path);
}
if (!context.process) {
@ -52,7 +61,9 @@ function setupContext(path, context, plugin) {
context.getContractAddress = ethers.utils.getContractAddress;
context.getIcapAddress = ethers.utils.getIcapAddress;
context.arrayify = ethers.utils.arrayify;
context.concat = ethers.utils.concat;
context.hexlify = ethers.utils.hexlify;
context.zeroPad = ethers.utils.zeroPad;
context.joinSignature = ethers.utils.joinSignature;
context.splitSignature = ethers.utils.splitSignature;
context.id = ethers.utils.id;
@ -71,6 +82,37 @@ function setupContext(path, context, plugin) {
context.toUtf8String = ethers.utils.toUtf8String;
}
const cli = new CLI("sandbox");
function prepareCode(code) {
let ast = babelParseExpression(code, {
createParenthesizedExpressions: true
});
// Crawl the AST, to compute needed source code manipulations
const insert = [];
const descend = function (node) {
if (node == null || typeof (node) !== "object") {
return;
}
if (Array.isArray(node)) {
return node.forEach(descend);
}
// We will add parenthesis around ObjectExpressions, which
// otherwise look like blocks
if (node.type === "ObjectExpression") {
insert.push({ char: "(", offset: node.start });
insert.push({ char: ")", offset: node.end });
}
Object.keys(node).forEach((key) => descend(key));
};
descend(ast);
// We make modifications from back to front, so we don't need
// to adjust offsets
insert.sort((a, b) => (b.offset - a.offset));
// Modify the code for REPL
insert.forEach((mod) => {
code = code.substring(0, mod.offset) + mod.char + code.substring(mod.offset);
});
return code;
}
class SandboxPlugin extends Plugin {
static getHelp() {
return {
@ -101,33 +143,59 @@ class SandboxPlugin extends Plugin {
});
}
run() {
console.log("network: " + this.network.name + " (chainId: " + this.network.chainId + ")");
let nextPromiseId = 0;
function promiseWriter(output) {
if (output instanceof Promise) {
repl.context._p = output;
let promiseId = nextPromiseId++;
output.then((result) => {
console.log(`\n<Promise id=${promiseId} resolved>`);
console.log(util.inspect(result));
repl.context._r = result;
repl.displayPrompt(true);
console.log(`version: ${ethers.version}`);
console.log(`network: ${this.network.name} (chainId: ${this.network.chainId})`);
const filename = resolve(process.cwd(), "./sandbox.js");
const prompt = (this.provider ? this.network.name : "no-network") + "> ";
const evaluate = function (code, context, file, _callback) {
// Pausing the stdin (which prompt does when it leaves), causes
// readline to end us. So, we always re-enable stdin on a result
const callback = (error, result) => {
_callback(error, result);
process.stdin.resume();
};
try {
code = prepareCode(code);
}
catch (error) {
if (error instanceof SyntaxError) {
const leftover = code.substring(error.pos);
const loc = error.loc;
if (leftover.trim()) {
// After the first line, the prompt is "... "
console.log(repeat("-", ((loc.line === 1) ? prompt.length : 4) + loc.column - 1) + "^");
console.log(`Syntax Error! ${error.message}`);
}
else {
error = new REPL.Recoverable(error);
}
}
return callback(error);
}
try {
const result = vm.runInContext(code, context, {
filename: filename
});
if (result instanceof Promise) {
result.then((result) => {
callback(null, result);
}, (error) => {
console.log(`\n<Promise id=${promiseId} rejected>`);
console.log(util.inspect(error));
repl.displayPrompt(true);
callback(error);
});
return `<Promise id=${promiseId} pending>`;
}
return util.inspect(output);
else {
callback(null, result);
}
let repl = REPL.start({
input: process.stdin,
output: process.stdout,
prompt: (this.provider ? this.network.name : "no-network") + "> ",
writer: promiseWriter
}
catch (error) {
callback(error);
}
};
const repl = REPL.start({
prompt: prompt,
eval: evaluate
});
setupContext(resolve(process.cwd(), "./sandbox.js"), repl.context, this);
setupContext(filename, repl.context, this);
return new Promise((resolve) => {
repl.on("exit", function () {
console.log("");

@ -39,7 +39,7 @@ export interface PluginType {
export declare abstract class Plugin {
network: ethers.providers.Network;
provider: ethers.providers.Provider;
accounts: Array<WrappedSigner>;
accounts: ReadonlyArray<WrappedSigner>;
mnemonicPassword: boolean;
_xxxMnemonicPasswordHard: boolean;
gasLimit: ethers.BigNumber;

@ -1 +1 @@
export declare const version = "cli/5.0.0-beta.150";
export declare const version = "cli/5.0.0-beta.151";

@ -1,3 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = "cli/5.0.0-beta.150";
exports.version = "cli/5.0.0-beta.151";

57
packages/cli/lib/bin/ethers-asm.js Normal file → Executable file

@ -85,14 +85,26 @@ var AssemblePlugin = /** @class */ (function (_super) {
};
AssemblePlugin.getOptionHelp = function () {
return [
{
name: "--define KEY=VALUE",
help: "provide assembler defines"
},
{
name: "--disassemble",
help: "Disassemble input bytecode"
},
{
name: "--define KEY=VALUE",
help: "provide assembler defines"
}
name: "--ignore-warnings",
help: "Ignore warnings"
},
{
name: "--pic",
help: "generate position independent code"
},
{
name: "--target LABEL",
help: "output LABEL bytecode (default: _)"
},
];
};
AssemblePlugin.prototype.prepareOptions = function (argParser) {
@ -114,6 +126,9 @@ var AssemblePlugin = /** @class */ (function (_super) {
});
// We are disassembling...
this.disassemble = argParser.consumeFlag("disassemble");
this.ignoreWarnings = argParser.consumeFlag("ignore-warnings");
this.pic = argParser.consumeFlag("pic");
this.target = argParser.consumeOption("target");
return [2 /*return*/];
}
});
@ -181,23 +196,49 @@ var AssemblePlugin = /** @class */ (function (_super) {
};
AssemblePlugin.prototype.run = function () {
return __awaiter(this, void 0, void 0, function () {
var _a, _b;
var ast, _a, _b, error_1;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
if (!this.disassemble) return [3 /*break*/, 1];
console.log(asm_1.formatBytecode(asm_1.disassemble(this.content)));
return [3 /*break*/, 3];
return [3 /*break*/, 4];
case 1:
_c.trys.push([1, 3, , 4]);
ast = asm_1.parse(this.content, {
ignoreWarnings: !!this.ignoreWarnings
});
_b = (_a = console).log;
return [4 /*yield*/, asm_1.assemble(asm_1.parse(this.content), {
return [4 /*yield*/, asm_1.assemble(ast, {
defines: this.defines,
filename: this.filename,
positionIndependentCode: this.pic,
target: (this.target || "_")
})];
case 2:
_b.apply(_a, [_c.sent()]);
_c.label = 3;
case 3: return [2 /*return*/];
return [3 /*break*/, 4];
case 3:
error_1 = _c.sent();
if (error_1.errors) {
(error_1.errors).forEach(function (error) {
if (error.severity === asm_1.SemanticErrorSeverity.error) {
console.log("Error: " + error.message + " (line: " + (error.node.location.line + 1) + ")");
}
else if (error.severity === asm_1.SemanticErrorSeverity.warning) {
console.log("Warning: " + error.message + " (line: " + (error.node.location.line + 1) + ")");
}
else {
console.log(error);
return;
}
});
}
else {
throw error_1;
}
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
});

@ -404,19 +404,6 @@ var AccountPlugin = /** @class */ (function (_super) {
});
});
};
AccountPlugin.prototype.prepareOptions = function (argParser) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, _super.prototype.prepareOptions.call(this, argParser)];
case 1:
_a.sent();
ethers_1.ethers.utils.defineReadOnly(this, "_wait", argParser.consumeFlag("wait"));
return [2 /*return*/];
}
});
});
};
AccountPlugin.prototype.prepareArgs = function (args) {
return __awaiter(this, void 0, void 0, function () {
var helpLine, params, command, i;

@ -56,12 +56,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
var fs_1 = __importDefault(require("fs"));
var path_1 = require("path");
var repl_1 = __importDefault(require("repl"));
var util_1 = __importDefault(require("util"));
var vm_1 = __importDefault(require("vm"));
var ethers_1 = require("ethers");
var parser_1 = require("@babel/parser");
var cli_1 = require("../cli");
var prompt_1 = require("../prompt");
var solc_1 = require("../solc");
function repeat(c, length) {
if (c.length === 0) {
throw new Error("too short");
}
var result = c;
while (result.length < length) {
result += result;
}
return result.substring(0, length);
}
function setupContext(path, context, plugin) {
context.provider = plugin.provider;
context.accounts = plugin.accounts;
@ -75,7 +85,6 @@ function setupContext(path, context, plugin) {
context.console = console;
}
if (!context.require) {
//context.require = _module.createRequireFromPath(path);
context.require = solc_1.customRequire(path);
}
if (!context.process) {
@ -96,7 +105,9 @@ function setupContext(path, context, plugin) {
context.getContractAddress = ethers_1.ethers.utils.getContractAddress;
context.getIcapAddress = ethers_1.ethers.utils.getIcapAddress;
context.arrayify = ethers_1.ethers.utils.arrayify;
context.concat = ethers_1.ethers.utils.concat;
context.hexlify = ethers_1.ethers.utils.hexlify;
context.zeroPad = ethers_1.ethers.utils.zeroPad;
context.joinSignature = ethers_1.ethers.utils.joinSignature;
context.splitSignature = ethers_1.ethers.utils.splitSignature;
context.id = ethers_1.ethers.utils.id;
@ -115,6 +126,37 @@ function setupContext(path, context, plugin) {
context.toUtf8String = ethers_1.ethers.utils.toUtf8String;
}
var cli = new cli_1.CLI("sandbox");
function prepareCode(code) {
var ast = parser_1.parseExpression(code, {
createParenthesizedExpressions: true
});
// Crawl the AST, to compute needed source code manipulations
var insert = [];
var descend = function (node) {
if (node == null || typeof (node) !== "object") {
return;
}
if (Array.isArray(node)) {
return node.forEach(descend);
}
// We will add parenthesis around ObjectExpressions, which
// otherwise look like blocks
if (node.type === "ObjectExpression") {
insert.push({ char: "(", offset: node.start });
insert.push({ char: ")", offset: node.end });
}
Object.keys(node).forEach(function (key) { return descend(key); });
};
descend(ast);
// We make modifications from back to front, so we don't need
// to adjust offsets
insert.sort(function (a, b) { return (b.offset - a.offset); });
// Modify the code for REPL
insert.forEach(function (mod) {
code = code.substring(0, mod.offset) + mod.char + code.substring(mod.offset);
});
return code;
}
var SandboxPlugin = /** @class */ (function (_super) {
__extends(SandboxPlugin, _super);
function SandboxPlugin() {
@ -166,33 +208,59 @@ var SandboxPlugin = /** @class */ (function (_super) {
});
};
SandboxPlugin.prototype.run = function () {
console.log("version: " + ethers_1.ethers.version);
console.log("network: " + this.network.name + " (chainId: " + this.network.chainId + ")");
var nextPromiseId = 0;
function promiseWriter(output) {
if (output instanceof Promise) {
repl.context._p = output;
var promiseId_1 = nextPromiseId++;
output.then(function (result) {
console.log("\n<Promise id=" + promiseId_1 + " resolved>");
console.log(util_1.default.inspect(result));
repl.context._r = result;
repl.displayPrompt(true);
var filename = path_1.resolve(process.cwd(), "./sandbox.js");
var prompt = (this.provider ? this.network.name : "no-network") + "> ";
var evaluate = function (code, context, file, _callback) {
// Pausing the stdin (which prompt does when it leaves), causes
// readline to end us. So, we always re-enable stdin on a result
var callback = function (error, result) {
_callback(error, result);
process.stdin.resume();
};
try {
code = prepareCode(code);
}
catch (error) {
if (error instanceof SyntaxError) {
var leftover = code.substring(error.pos);
var loc = error.loc;
if (leftover.trim()) {
// After the first line, the prompt is "... "
console.log(repeat("-", ((loc.line === 1) ? prompt.length : 4) + loc.column - 1) + "^");
console.log("Syntax Error! " + error.message);
}
else {
error = new repl_1.default.Recoverable(error);
}
}
return callback(error);
}
try {
var result = vm_1.default.runInContext(code, context, {
filename: filename
});
if (result instanceof Promise) {
result.then(function (result) {
callback(null, result);
}, function (error) {
console.log("\n<Promise id=" + promiseId_1 + " rejected>");
console.log(util_1.default.inspect(error));
repl.displayPrompt(true);
callback(error);
});
return "<Promise id=" + promiseId_1 + " pending>";
}
return util_1.default.inspect(output);
else {
callback(null, result);
}
}
catch (error) {
callback(error);
}
};
var repl = repl_1.default.start({
input: process.stdin,
output: process.stdout,
prompt: (this.provider ? this.network.name : "no-network") + "> ",
writer: promiseWriter
prompt: prompt,
eval: evaluate
});
setupContext(path_1.resolve(process.cwd(), "./sandbox.js"), repl.context, this);
setupContext(filename, repl.context, this);
return new Promise(function (resolve) {
repl.on("exit", function () {
console.log("");

@ -39,7 +39,7 @@ export interface PluginType {
export declare abstract class Plugin {
network: ethers.providers.Network;
provider: ethers.providers.Provider;
accounts: Array<WrappedSigner>;
accounts: ReadonlyArray<WrappedSigner>;
mnemonicPassword: boolean;
_xxxMnemonicPasswordHard: boolean;
gasLimit: ethers.BigNumber;

@ -8,6 +8,7 @@
},
"dependencies": {
"@babel/parser": "7.8.4",
"@babel/types": "7.8.3",
"@ethersproject/asm": ">=5.0.0-beta.148",
"@ethersproject/basex": ">=5.0.0-beta.127",
"ethers": ">=5.0.0-beta.156",
@ -40,7 +41,7 @@
"scripts": {
"test": "exit 1"
},
"tarballHash": "0xf20ecfe28643c5e68c573b3204e4a03e02494722929d2fa2467d800ef0ff6edb",
"tarballHash": "0xf7cdf17b34ec310a5d6e2f93116a70787764532f9c9a11d7dcb5484ed44d731d",
"types": "./lib/index.d.ts",
"version": "5.0.0-beta.150"
"version": "5.0.0-beta.151"
}

@ -1 +1 @@
export const version = "cli/5.0.0-beta.150";
export const version = "cli/5.0.0-beta.151";

@ -180,6 +180,8 @@ class AssemblePlugin extends Plugin {
return;
}
});
} else {
throw error;
}
}
}

@ -57,7 +57,9 @@ function setupContext(path: string, context: any, plugin: Plugin) {
context.getIcapAddress = ethers.utils.getIcapAddress;
context.arrayify = ethers.utils.arrayify;
context.concat = ethers.utils.concat;
context.hexlify = ethers.utils.hexlify;
context.zeroPad = ethers.utils.zeroPad;
context.joinSignature = ethers.utils.joinSignature;
context.splitSignature = ethers.utils.splitSignature;

@ -1,7 +1,7 @@
"use strict";
import { EventFragment, Fragment, Indexed, Interface, JsonFragment, ParamType, Result } from "@ethersproject/abi";
import { Block, BlockTag, Listener, Log, Provider, TransactionReceipt, TransactionRequest, TransactionResponse } from "@ethersproject/abstract-provider";
import { Block, BlockTag, Filter, FilterByBlockHash, Listener, Log, Provider, TransactionReceipt, TransactionRequest, TransactionResponse } from "@ethersproject/abstract-provider";
import { Signer, VoidSigner } from "@ethersproject/abstract-signer";
import { getContractAddress } from "@ethersproject/address";
import { BigNumber, BigNumberish } from "@ethersproject/bignumber";
@ -780,10 +780,10 @@ export class Contract {
if (toBlock != null) {
logger.throwArgumentError("cannot specify toBlock with blockhash", "toBlock", toBlock);
}
filter.blockhash = fromBlockOrBlockhash;
(<FilterByBlockHash>filter).blockhash = fromBlockOrBlockhash;
} else {
filter.fromBlock = ((fromBlockOrBlockhash != null) ? fromBlockOrBlockhash: 0);
filter.toBlock = ((toBlock != null) ? toBlock: "latest");
(<Filter>filter).fromBlock = ((fromBlockOrBlockhash != null) ? fromBlockOrBlockhash: 0);
(<Filter>filter).toBlock = ((toBlock != null) ? toBlock: "latest");
}
return this.provider.getLogs(filter).then((logs) => {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -4629,7 +4629,7 @@ class FixedNumber {
}
}
const version$3 = "properties/5.0.0-beta.136";
const version$3 = "properties/5.0.0-beta.137";
"use strict";
const logger$3 = new Logger(version$3);
@ -4665,10 +4665,10 @@ function resolveProperties(object) {
});
return Promise.all(promises).then((results) => {
const result = {};
return results.reduce((accum, result) => {
return (results.reduce((accum, result) => {
accum[result.key] = result.value;
return accum;
}, result);
}, result));
});
}
function checkProperties(object, properties) {
@ -4691,7 +4691,7 @@ function shallowCopy(object) {
const opaque = { bigint: true, boolean: true, number: true, string: true };
// Returns a new copy of object, such that no properties may be replaced.
// New properties may be added only to objects.
function deepCopy(object) {
function _deepCopy(object) {
// Opaque objects are not mutable, so safe to copy by assignment
if (object === undefined || object === null || opaque[typeof (object)]) {
return object;
@ -4719,7 +4719,10 @@ function deepCopy(object) {
if (typeof (object) === "function") {
return object;
}
throw new Error("Cannot deepCopy " + typeof (object));
logger$3.throwArgumentError(`Cannot deepCopy ${typeof (object)}`, "object", object);
}
function deepCopy(object) {
return _deepCopy(object);
}
class Description {
constructor(info) {
@ -4739,7 +4742,7 @@ var lib_esm$2 = /*#__PURE__*/Object.freeze({
Description: Description
});
const version$4 = "abi/5.0.0-beta.144";
const version$4 = "abi/5.0.0-beta.145";
"use strict";
const logger$4 = new Logger(version$4);
@ -7715,7 +7718,7 @@ class Provider {
}
}
const version$9 = "abstract-signer/5.0.0-beta.139";
const version$9 = "abstract-signer/5.0.0-beta.140";
"use strict";
var __awaiter = (window && window.__awaiter) || function (thisArg, _arguments, P, generator) {
@ -7838,25 +7841,9 @@ class Signer {
if (tx.nonce == null) {
tx.nonce = this.getTransactionCount("pending");
}
/*
// checkTransaction does this...
if (tx.from == null) {
tx.from = this.getAddress();
} else {
tx.from = Promise.all([
this.getAddress(),
this.provider.resolveName(tx.from)
]).then((results) => {
if (results[0] !== results[1]) {
logger.throwArgumentError("from address mismatch", "transaction", transaction);
}
return results[0];
});
}
*/
if (tx.gasLimit == null) {
tx.gasLimit = this.estimateGas(tx).catch((error) => {
logger$d.throwError("cannot estimate gas; transaction may fail or may require manual gas limit", Logger.errors.UNPREDICTABLE_GAS_LIMIT, {
return logger$d.throwError("cannot estimate gas; transaction may fail or may require manual gas limit", Logger.errors.UNPREDICTABLE_GAS_LIMIT, {
tx: tx
});
});
@ -15111,7 +15098,7 @@ function decryptJsonWallet(json, password, progressCallback) {
return Promise.reject(new Error("invalid JSON wallet"));
}
const version$g = "wallet/5.0.0-beta.136";
const version$g = "wallet/5.0.0-beta.137";
"use strict";
const logger$k = new Logger(version$g);
@ -15148,7 +15135,6 @@ class Wallet extends Signer {
}
else {
defineReadOnly(this, "_mnemonic", () => null);
defineReadOnly(this, "path", null);
}
}
else {
@ -15163,7 +15149,6 @@ class Wallet extends Signer {
defineReadOnly(this, "_signingKey", () => signingKey);
}
defineReadOnly(this, "_mnemonic", () => null);
defineReadOnly(this, "path", null);
defineReadOnly(this, "address", computeAddress(this.publicKey));
}
if (provider && !Provider.isProvider(provider)) {
@ -16265,7 +16250,7 @@ function poll(func, options) {
});
}
const version$j = "providers/5.0.0-beta.152";
const version$j = "providers/5.0.0-beta.153";
"use strict";
const logger$n = new Logger(version$j);
@ -19362,7 +19347,7 @@ var utils$1 = /*#__PURE__*/Object.freeze({
Indexed: Indexed
});
const version$l = "ethers/5.0.0-beta.172";
const version$l = "ethers/5.0.0-beta.173";
"use strict";
const errors = Logger.errors;

File diff suppressed because one or more lines are too long

@ -4757,7 +4757,7 @@
var _version$6 = createCommonjsModule(function (module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = "properties/5.0.0-beta.136";
exports.version = "properties/5.0.0-beta.137";
});
var _version$7 = unwrapExports(_version$6);
@ -4803,10 +4803,10 @@
});
return Promise.all(promises).then(function (results) {
var result = {};
return results.reduce(function (accum, result) {
return (results.reduce(function (accum, result) {
accum[result.key] = result.value;
return accum;
}, result);
}, result));
});
}
exports.resolveProperties = resolveProperties;
@ -4832,7 +4832,7 @@
var opaque = { bigint: true, boolean: true, number: true, string: true };
// Returns a new copy of object, such that no properties may be replaced.
// New properties may be added only to objects.
function deepCopy(object) {
function _deepCopy(object) {
// Opaque objects are not mutable, so safe to copy by assignment
if (object === undefined || object === null || opaque[typeof (object)]) {
return object;
@ -4860,7 +4860,10 @@
if (typeof (object) === "function") {
return object;
}
throw new Error("Cannot deepCopy " + typeof (object));
logger.throwArgumentError("Cannot deepCopy " + typeof (object), "object", object);
}
function deepCopy(object) {
return _deepCopy(object);
}
exports.deepCopy = deepCopy;
var Description = /** @class */ (function () {
@ -4886,7 +4889,7 @@
var _version$8 = createCommonjsModule(function (module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = "abi/5.0.0-beta.144";
exports.version = "abi/5.0.0-beta.145";
});
var _version$9 = unwrapExports(_version$8);
@ -8545,7 +8548,7 @@
var _version$i = createCommonjsModule(function (module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = "abstract-signer/5.0.0-beta.139";
exports.version = "abstract-signer/5.0.0-beta.140";
});
var _version$j = unwrapExports(_version$i);
@ -8727,25 +8730,9 @@
if (tx.nonce == null) {
tx.nonce = this.getTransactionCount("pending");
}
/*
// checkTransaction does this...
if (tx.from == null) {
tx.from = this.getAddress();
} else {
tx.from = Promise.all([
this.getAddress(),
this.provider.resolveName(tx.from)
]).then((results) => {
if (results[0] !== results[1]) {
logger.throwArgumentError("from address mismatch", "transaction", transaction);
}
return results[0];
});
}
*/
if (tx.gasLimit == null) {
tx.gasLimit = this.estimateGas(tx).catch(function (error) {
logger.throwError("cannot estimate gas; transaction may fail or may require manual gas limit", lib.Logger.errors.UNPREDICTABLE_GAS_LIMIT, {
return logger.throwError("cannot estimate gas; transaction may fail or may require manual gas limit", lib.Logger.errors.UNPREDICTABLE_GAS_LIMIT, {
tx: tx
});
});
@ -16414,7 +16401,7 @@
var _version$A = createCommonjsModule(function (module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = "wallet/5.0.0-beta.136";
exports.version = "wallet/5.0.0-beta.137";
});
var _version$B = unwrapExports(_version$A);
@ -16487,7 +16474,6 @@
}
else {
lib$3.defineReadOnly(_this, "_mnemonic", function () { return null; });
lib$3.defineReadOnly(_this, "path", null);
}
}
else {
@ -16502,7 +16488,6 @@
lib$3.defineReadOnly(_this, "_signingKey", function () { return signingKey_2; });
}
lib$3.defineReadOnly(_this, "_mnemonic", function () { return null; });
lib$3.defineReadOnly(_this, "path", null);
lib$3.defineReadOnly(_this, "address", lib$g.computeAddress(_this.publicKey));
}
if (provider && !lib$b.Provider.isProvider(provider)) {
@ -17710,7 +17695,7 @@
var _version$G = createCommonjsModule(function (module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = "providers/5.0.0-beta.152";
exports.version = "providers/5.0.0-beta.153";
});
var _version$H = unwrapExports(_version$G);
@ -21875,7 +21860,7 @@
var _version$K = createCommonjsModule(function (module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = "ethers/5.0.0-beta.172";
exports.version = "ethers/5.0.0-beta.173";
});
var _version$L = unwrapExports(_version$K);

File diff suppressed because one or more lines are too long

@ -1 +1 @@
export declare const version = "ethers/5.0.0-beta.172";
export declare const version = "ethers/5.0.0-beta.173";

@ -1 +1 @@
export const version = "ethers/5.0.0-beta.172";
export const version = "ethers/5.0.0-beta.173";

@ -20,10 +20,11 @@ import { verifyMessage } from "@ethersproject/wallet";
import { fetchJson, poll } from "@ethersproject/web";
import { SupportedAlgorithm } from "@ethersproject/sha2";
import { UnicodeNormalizationForm, Utf8ErrorReason } from "@ethersproject/strings";
import { UnsignedTransaction } from "@ethersproject/transactions";
import { CoerceFunc } from "@ethersproject/abi";
import { Bytes, BytesLike, Hexable } from "@ethersproject/bytes";
import { Mnemonic } from "@ethersproject/hdnode";
import { EncryptOptions, ProgressCallback } from "@ethersproject/json-wallets";
import { Utf8ErrorFunc } from "@ethersproject/strings";
import { ConnectionInfo, FetchJsonResponse, OnceBlockable, PollOptions } from "@ethersproject/web";
export { AbiCoder, defaultAbiCoder, Fragment, EventFragment, FunctionFragment, ParamType, FormatTypes, Logger, RLP, fetchJson, poll, checkProperties, deepCopy, defineReadOnly, getStatic, resolveProperties, shallowCopy, arrayify, concat, stripZeros, zeroPad, isBytes, isBytesLike, defaultPath, HDNode, SigningKey, Interface, base64, hexlify, isHexString, hexStripZeros, hexValue, hexZeroPad, hexDataLength, hexDataSlice, nameprep, _toEscapedUtf8String, toUtf8Bytes, toUtf8CodePoints, toUtf8String, Utf8ErrorFuncs, formatBytes32String, parseBytes32String, hashMessage, namehash, isValidName, id, getAddress, getIcapAddress, getContractAddress, getCreate2Address, isAddress, formatEther, parseEther, formatUnits, parseUnits, commify, keccak256, sha256, randomBytes, solidityPack, solidityKeccak256, soliditySha256, splitSignature, joinSignature, parseTransaction, serializeTransaction, getJsonWalletAddress, computeAddress, recoverAddress, computePublicKey, recoverPublicKey, verifyMessage, mnemonicToEntropy, entropyToMnemonic, isValidMnemonic, mnemonicToSeed, SupportedAlgorithm, UnicodeNormalizationForm, Utf8ErrorReason, Bytes, BytesLike, Hexable, CoerceFunc, Indexed, Mnemonic, Utf8ErrorFunc, ConnectionInfo, OnceBlockable, PollOptions, FetchJsonResponse, EncryptOptions, ProgressCallback };
export { AbiCoder, defaultAbiCoder, Fragment, EventFragment, FunctionFragment, ParamType, FormatTypes, Logger, RLP, fetchJson, poll, checkProperties, deepCopy, defineReadOnly, getStatic, resolveProperties, shallowCopy, arrayify, concat, stripZeros, zeroPad, isBytes, isBytesLike, defaultPath, HDNode, SigningKey, Interface, base64, hexlify, isHexString, hexStripZeros, hexValue, hexZeroPad, hexDataLength, hexDataSlice, nameprep, _toEscapedUtf8String, toUtf8Bytes, toUtf8CodePoints, toUtf8String, Utf8ErrorFuncs, formatBytes32String, parseBytes32String, hashMessage, namehash, isValidName, id, getAddress, getIcapAddress, getContractAddress, getCreate2Address, isAddress, formatEther, parseEther, formatUnits, parseUnits, commify, keccak256, sha256, randomBytes, solidityPack, solidityKeccak256, soliditySha256, splitSignature, joinSignature, parseTransaction, serializeTransaction, getJsonWalletAddress, computeAddress, recoverAddress, computePublicKey, recoverPublicKey, verifyMessage, mnemonicToEntropy, entropyToMnemonic, isValidMnemonic, mnemonicToSeed, SupportedAlgorithm, UnicodeNormalizationForm, Utf8ErrorReason, Bytes, BytesLike, Hexable, UnsignedTransaction, CoerceFunc, Indexed, Mnemonic, Utf8ErrorFunc, ConnectionInfo, OnceBlockable, PollOptions, FetchJsonResponse, EncryptOptions, ProgressCallback };

@ -1 +1 @@
export declare const version = "ethers/5.0.0-beta.172";
export declare const version = "ethers/5.0.0-beta.173";

@ -1,3 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = "ethers/5.0.0-beta.172";
exports.version = "ethers/5.0.0-beta.173";

@ -20,10 +20,11 @@ import { verifyMessage } from "@ethersproject/wallet";
import { fetchJson, poll } from "@ethersproject/web";
import { SupportedAlgorithm } from "@ethersproject/sha2";
import { UnicodeNormalizationForm, Utf8ErrorReason } from "@ethersproject/strings";
import { UnsignedTransaction } from "@ethersproject/transactions";
import { CoerceFunc } from "@ethersproject/abi";
import { Bytes, BytesLike, Hexable } from "@ethersproject/bytes";
import { Mnemonic } from "@ethersproject/hdnode";
import { EncryptOptions, ProgressCallback } from "@ethersproject/json-wallets";
import { Utf8ErrorFunc } from "@ethersproject/strings";
import { ConnectionInfo, FetchJsonResponse, OnceBlockable, PollOptions } from "@ethersproject/web";
export { AbiCoder, defaultAbiCoder, Fragment, EventFragment, FunctionFragment, ParamType, FormatTypes, Logger, RLP, fetchJson, poll, checkProperties, deepCopy, defineReadOnly, getStatic, resolveProperties, shallowCopy, arrayify, concat, stripZeros, zeroPad, isBytes, isBytesLike, defaultPath, HDNode, SigningKey, Interface, base64, hexlify, isHexString, hexStripZeros, hexValue, hexZeroPad, hexDataLength, hexDataSlice, nameprep, _toEscapedUtf8String, toUtf8Bytes, toUtf8CodePoints, toUtf8String, Utf8ErrorFuncs, formatBytes32String, parseBytes32String, hashMessage, namehash, isValidName, id, getAddress, getIcapAddress, getContractAddress, getCreate2Address, isAddress, formatEther, parseEther, formatUnits, parseUnits, commify, keccak256, sha256, randomBytes, solidityPack, solidityKeccak256, soliditySha256, splitSignature, joinSignature, parseTransaction, serializeTransaction, getJsonWalletAddress, computeAddress, recoverAddress, computePublicKey, recoverPublicKey, verifyMessage, mnemonicToEntropy, entropyToMnemonic, isValidMnemonic, mnemonicToSeed, SupportedAlgorithm, UnicodeNormalizationForm, Utf8ErrorReason, Bytes, BytesLike, Hexable, CoerceFunc, Indexed, Mnemonic, Utf8ErrorFunc, ConnectionInfo, OnceBlockable, PollOptions, FetchJsonResponse, EncryptOptions, ProgressCallback };
export { AbiCoder, defaultAbiCoder, Fragment, EventFragment, FunctionFragment, ParamType, FormatTypes, Logger, RLP, fetchJson, poll, checkProperties, deepCopy, defineReadOnly, getStatic, resolveProperties, shallowCopy, arrayify, concat, stripZeros, zeroPad, isBytes, isBytesLike, defaultPath, HDNode, SigningKey, Interface, base64, hexlify, isHexString, hexStripZeros, hexValue, hexZeroPad, hexDataLength, hexDataSlice, nameprep, _toEscapedUtf8String, toUtf8Bytes, toUtf8CodePoints, toUtf8String, Utf8ErrorFuncs, formatBytes32String, parseBytes32String, hashMessage, namehash, isValidName, id, getAddress, getIcapAddress, getContractAddress, getCreate2Address, isAddress, formatEther, parseEther, formatUnits, parseUnits, commify, keccak256, sha256, randomBytes, solidityPack, solidityKeccak256, soliditySha256, splitSignature, joinSignature, parseTransaction, serializeTransaction, getJsonWalletAddress, computeAddress, recoverAddress, computePublicKey, recoverPublicKey, verifyMessage, mnemonicToEntropy, entropyToMnemonic, isValidMnemonic, mnemonicToSeed, SupportedAlgorithm, UnicodeNormalizationForm, Utf8ErrorReason, Bytes, BytesLike, Hexable, UnsignedTransaction, CoerceFunc, Indexed, Mnemonic, Utf8ErrorFunc, ConnectionInfo, OnceBlockable, PollOptions, FetchJsonResponse, EncryptOptions, ProgressCallback };

@ -52,7 +52,7 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"tarballHash": "0x8bf3df33da732d3a07a569560633f98fd9b7826cfc306fb4f3c159f98873b29c",
"tarballHash": "0x6eba00cc347e154a1bacd6b073af290f2da00b3e1e0b06e4ea342d94940832be",
"types": "./lib/index.d.ts",
"version": "5.0.0-beta.172"
"version": "5.0.0-beta.173"
}

@ -1 +1 @@
export const version = "ethers/5.0.0-beta.172";
export const version = "ethers/5.0.0-beta.173";

@ -26,7 +26,7 @@ import { fetchJson, poll } from "@ethersproject/web";
import { SupportedAlgorithm } from "@ethersproject/sha2";
import { UnicodeNormalizationForm, Utf8ErrorReason } from "@ethersproject/strings";
import { UnsignedTransaction } from "@ethersproject/transactions";
////////////////////////
// Types and Interfaces
@ -165,6 +165,8 @@ export {
BytesLike,
Hexable,
UnsignedTransaction,
CoerceFunc,
Indexed,

@ -95,11 +95,11 @@ export class LedgerSigner extends ethers.Signer {
async signTransaction(transaction: ethers.providers.TransactionRequest): Promise<string> {
const tx = transaction = await ethers.utils.resolveProperties(transaction);
const unsignedTx = ethers.utils.serializeTransaction(tx).substring(2);
const unsignedTx = ethers.utils.serializeTransaction(<ethers.utils.UnsignedTransaction>tx).substring(2);
const sig = await this._retry((eth) => eth.signTransaction(this.path, unsignedTx));
return ethers.utils.serializeTransaction(tx, {
return ethers.utils.serializeTransaction(<ethers.utils.UnsignedTransaction>tx, {
v: sig.v,
r: ("0x" + sig.r),
s: ("0x" + sig.s),

@ -1 +1 @@
export declare const version = "properties/5.0.0-beta.136";
export declare const version = "properties/5.0.0-beta.137";

@ -1 +1 @@
export const version = "properties/5.0.0-beta.136";
export const version = "properties/5.0.0-beta.137";

@ -1,11 +1,17 @@
export declare function defineReadOnly(object: any, name: string, value: any): void;
export declare function defineReadOnly<T, K extends keyof T>(object: T, name: K, value: T[K]): void;
export declare function getStatic<T>(ctor: any, key: string): T;
export declare function resolveProperties(object: any): Promise<any>;
export declare type Similar<T> = {
[P in keyof T]: T[P];
};
export declare type Resolvable<T> = {
[P in keyof T]: T[P] | Promise<T[P]>;
};
export declare function resolveProperties<T>(object: Resolvable<T>): Promise<Similar<T>>;
export declare function checkProperties(object: any, properties: {
[name: string]: boolean;
}): void;
export declare function shallowCopy(object: any): any;
export declare function deepCopy(object: any): any;
export declare function shallowCopy<T>(object: T): Similar<T>;
export declare function deepCopy<T>(object: T): Similar<T>;
export declare class Description<T = any> {
constructor(info: T);
}

@ -34,10 +34,10 @@ export function resolveProperties(object) {
});
return Promise.all(promises).then((results) => {
const result = {};
return results.reduce((accum, result) => {
return (results.reduce((accum, result) => {
accum[result.key] = result.value;
return accum;
}, result);
}, result));
});
}
export function checkProperties(object, properties) {
@ -60,7 +60,7 @@ export function shallowCopy(object) {
const opaque = { bigint: true, boolean: true, number: true, string: true };
// Returns a new copy of object, such that no properties may be replaced.
// New properties may be added only to objects.
export function deepCopy(object) {
function _deepCopy(object) {
// Opaque objects are not mutable, so safe to copy by assignment
if (object === undefined || object === null || opaque[typeof (object)]) {
return object;
@ -88,7 +88,10 @@ export function deepCopy(object) {
if (typeof (object) === "function") {
return object;
}
throw new Error("Cannot deepCopy " + typeof (object));
logger.throwArgumentError(`Cannot deepCopy ${typeof (object)}`, "object", object);
}
export function deepCopy(object) {
return _deepCopy(object);
}
export class Description {
constructor(info) {

@ -1 +1 @@
export declare const version = "properties/5.0.0-beta.136";
export declare const version = "properties/5.0.0-beta.137";

@ -1,3 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = "properties/5.0.0-beta.136";
exports.version = "properties/5.0.0-beta.137";

@ -1,11 +1,17 @@
export declare function defineReadOnly(object: any, name: string, value: any): void;
export declare function defineReadOnly<T, K extends keyof T>(object: T, name: K, value: T[K]): void;
export declare function getStatic<T>(ctor: any, key: string): T;
export declare function resolveProperties(object: any): Promise<any>;
export declare type Similar<T> = {
[P in keyof T]: T[P];
};
export declare type Resolvable<T> = {
[P in keyof T]: T[P] | Promise<T[P]>;
};
export declare function resolveProperties<T>(object: Resolvable<T>): Promise<Similar<T>>;
export declare function checkProperties(object: any, properties: {
[name: string]: boolean;
}): void;
export declare function shallowCopy(object: any): any;
export declare function deepCopy(object: any): any;
export declare function shallowCopy<T>(object: T): Similar<T>;
export declare function deepCopy<T>(object: T): Similar<T>;
export declare class Description<T = any> {
constructor(info: T);
}

@ -37,10 +37,10 @@ function resolveProperties(object) {
});
return Promise.all(promises).then(function (results) {
var result = {};
return results.reduce(function (accum, result) {
return (results.reduce(function (accum, result) {
accum[result.key] = result.value;
return accum;
}, result);
}, result));
});
}
exports.resolveProperties = resolveProperties;
@ -66,7 +66,7 @@ exports.shallowCopy = shallowCopy;
var opaque = { bigint: true, boolean: true, number: true, string: true };
// Returns a new copy of object, such that no properties may be replaced.
// New properties may be added only to objects.
function deepCopy(object) {
function _deepCopy(object) {
// Opaque objects are not mutable, so safe to copy by assignment
if (object === undefined || object === null || opaque[typeof (object)]) {
return object;
@ -94,7 +94,10 @@ function deepCopy(object) {
if (typeof (object) === "function") {
return object;
}
throw new Error("Cannot deepCopy " + typeof (object));
logger.throwArgumentError("Cannot deepCopy " + typeof (object), "object", object);
}
function deepCopy(object) {
return _deepCopy(object);
}
exports.deepCopy = deepCopy;
var Description = /** @class */ (function () {

@ -25,7 +25,7 @@
"build": "tsc -p ./tsconfig.json",
"test": "echo \"Error: no test specified\" && exit 1"
},
"tarballHash": "0x6a1817252456f7b6cbd2edd780d3b2585fcb477da55f7e7282869c0a5a7f5184",
"tarballHash": "0xfbf63fe67dfb09b4e1d9cf19010f61f540f63105cc006a148d91ab9bbf016c61",
"types": "./lib/index.d.ts",
"version": "5.0.0-beta.136"
"version": "5.0.0-beta.137"
}

@ -1 +1 @@
export const version = "properties/5.0.0-beta.136";
export const version = "properties/5.0.0-beta.137";

@ -22,11 +22,19 @@ export function getStatic<T>(ctor: any, key: string): T {
return null;
}
export type Similar<T> = {
[P in keyof T]: T[P];
}
export type Resolvable<T> = {
[P in keyof T]: T[P] | Promise<T[P]>;
}
type Result = { key: string, value: any};
export function resolveProperties(object: any): Promise<any> {
export function resolveProperties<T>(object: Resolvable<T>): Promise<Similar<T>> {
const promises: Array<Promise<Result>> = Object.keys(object).map((key) => {
const value = object[key];
const value = (<any>object)[key];
if (!(value instanceof Promise)) {
return Promise.resolve({ key: key, value: value });
@ -39,10 +47,10 @@ export function resolveProperties(object: any): Promise<any> {
return Promise.all(promises).then((results) => {
const result: any = { };
return results.reduce((accum, result) => {
return (<Similar<T>>(results.reduce((accum, result) => {
accum[result.key] = result.value;
return accum;
}, result);
}, result)));
});
}
@ -58,7 +66,7 @@ export function checkProperties(object: any, properties: { [ name: string ]: boo
});
}
export function shallowCopy(object: any): any {
export function shallowCopy<T>(object: T): Similar<T> {
const result: any = {};
for (const key in object) { result[key] = object[key]; }
return result;
@ -68,7 +76,7 @@ const opaque: { [key: string]: boolean } = { bigint: true, boolean: true, number
// Returns a new copy of object, such that no properties may be replaced.
// New properties may be added only to objects.
export function deepCopy(object: any): any {
function _deepCopy(object: any): any {
// Opaque objects are not mutable, so safe to copy by assignment
if (object === undefined || object === null || opaque[typeof(object)]) { return object; }
@ -98,7 +106,11 @@ export function deepCopy(object: any): any {
return object;
}
throw new Error("Cannot deepCopy " + typeof(object));
logger.throwArgumentError(`Cannot deepCopy ${ typeof(object) }`, "object", object);
}
export function deepCopy<T>(object: T): Similar<T> {
return _deepCopy(object);
}
export class Description<T = any> {

@ -1 +1 @@
export declare const version = "providers/5.0.0-beta.152";
export declare const version = "providers/5.0.0-beta.153";

@ -1 +1 @@
export const version = "providers/5.0.0-beta.152";
export const version = "providers/5.0.0-beta.153";

@ -7,7 +7,7 @@ export interface FallbackProviderConfig {
weight?: number;
}
export declare class FallbackProvider extends BaseProvider {
readonly providerConfigs: Array<FallbackProviderConfig>;
readonly providerConfigs: ReadonlyArray<FallbackProviderConfig>;
readonly quorum: number;
_highestBlockNumber: number;
constructor(providers: Array<Provider | FallbackProviderConfig>, quorum?: number);

@ -1 +1 @@
export declare const version = "providers/5.0.0-beta.152";
export declare const version = "providers/5.0.0-beta.153";

@ -1,3 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = "providers/5.0.0-beta.152";
exports.version = "providers/5.0.0-beta.153";

@ -7,7 +7,7 @@ export interface FallbackProviderConfig {
weight?: number;
}
export declare class FallbackProvider extends BaseProvider {
readonly providerConfigs: Array<FallbackProviderConfig>;
readonly providerConfigs: ReadonlyArray<FallbackProviderConfig>;
readonly quorum: number;
_highestBlockNumber: number;
constructor(providers: Array<Provider | FallbackProviderConfig>, quorum?: number);

@ -44,7 +44,7 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"tarballHash": "0xce9a626d62434fe628a5ab60fd3a05198eda8d7cf7ff7d54b63096e5b95d918c",
"tarballHash": "0x54990412c71ac1e81ce962062a2bbcc0127a32b1243f1db28fbb2af511b3e749",
"types": "./lib/index.d.ts",
"version": "5.0.0-beta.152"
"version": "5.0.0-beta.153"
}

@ -1 +1 @@
export const version = "providers/5.0.0-beta.152";
export const version = "providers/5.0.0-beta.153";

@ -1 +1 @@
export declare const version = "wallet/5.0.0-beta.136";
export declare const version = "wallet/5.0.0-beta.137";

@ -1 +1 @@
export const version = "wallet/5.0.0-beta.136";
export const version = "wallet/5.0.0-beta.137";

@ -47,7 +47,6 @@ export class Wallet extends Signer {
}
else {
defineReadOnly(this, "_mnemonic", () => null);
defineReadOnly(this, "path", null);
}
}
else {
@ -62,7 +61,6 @@ export class Wallet extends Signer {
defineReadOnly(this, "_signingKey", () => signingKey);
}
defineReadOnly(this, "_mnemonic", () => null);
defineReadOnly(this, "path", null);
defineReadOnly(this, "address", computeAddress(this.publicKey));
}
if (provider && !Provider.isProvider(provider)) {

@ -1 +1 @@
export declare const version = "wallet/5.0.0-beta.136";
export declare const version = "wallet/5.0.0-beta.137";

Some files were not shown because too many files have changed in this diff Show More