contract.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953
  1. import { Interface, Typed } from "../abi/index.js";
  2. import { isAddressable, resolveAddress } from "../address/index.js";
  3. // import from provider.ts instead of index.ts to prevent circular dep
  4. // from EtherscanProvider
  5. import { copyRequest, Log } from "../providers/provider.js";
  6. import { defineProperties, getBigInt, isCallException, isHexString, resolveProperties, isError, makeError, assert, assertArgument } from "../utils/index.js";
  7. import { ContractEventPayload, ContractUnknownEventPayload, ContractTransactionResponse, EventLog, UndecodedEventLog } from "./wrappers.js";
  8. const BN_0 = BigInt(0);
  9. function canCall(value) {
  10. return (value && typeof (value.call) === "function");
  11. }
  12. function canEstimate(value) {
  13. return (value && typeof (value.estimateGas) === "function");
  14. }
  15. function canResolve(value) {
  16. return (value && typeof (value.resolveName) === "function");
  17. }
  18. function canSend(value) {
  19. return (value && typeof (value.sendTransaction) === "function");
  20. }
  21. function getResolver(value) {
  22. if (value != null) {
  23. if (canResolve(value)) {
  24. return value;
  25. }
  26. if (value.provider) {
  27. return value.provider;
  28. }
  29. }
  30. return undefined;
  31. }
  32. class PreparedTopicFilter {
  33. #filter;
  34. fragment;
  35. constructor(contract, fragment, args) {
  36. defineProperties(this, { fragment });
  37. if (fragment.inputs.length < args.length) {
  38. throw new Error("too many arguments");
  39. }
  40. // Recursively descend into args and resolve any addresses
  41. const runner = getRunner(contract.runner, "resolveName");
  42. const resolver = canResolve(runner) ? runner : null;
  43. this.#filter = (async function () {
  44. const resolvedArgs = await Promise.all(fragment.inputs.map((param, index) => {
  45. const arg = args[index];
  46. if (arg == null) {
  47. return null;
  48. }
  49. return param.walkAsync(args[index], (type, value) => {
  50. if (type === "address") {
  51. if (Array.isArray(value)) {
  52. return Promise.all(value.map((v) => resolveAddress(v, resolver)));
  53. }
  54. return resolveAddress(value, resolver);
  55. }
  56. return value;
  57. });
  58. }));
  59. return contract.interface.encodeFilterTopics(fragment, resolvedArgs);
  60. })();
  61. }
  62. getTopicFilter() {
  63. return this.#filter;
  64. }
  65. }
  66. // A = Arguments passed in as a tuple
  67. // R = The result type of the call (i.e. if only one return type,
  68. // the qualified type, otherwise Result)
  69. // D = The type the default call will return (i.e. R for view/pure,
  70. // TransactionResponse otherwise)
  71. //export interface ContractMethod<A extends Array<any> = Array<any>, R = any, D extends R | ContractTransactionResponse = ContractTransactionResponse> {
  72. function getRunner(value, feature) {
  73. if (value == null) {
  74. return null;
  75. }
  76. if (typeof (value[feature]) === "function") {
  77. return value;
  78. }
  79. if (value.provider && typeof (value.provider[feature]) === "function") {
  80. return value.provider;
  81. }
  82. return null;
  83. }
  84. function getProvider(value) {
  85. if (value == null) {
  86. return null;
  87. }
  88. return value.provider || null;
  89. }
  90. /**
  91. * @_ignore:
  92. */
  93. export async function copyOverrides(arg, allowed) {
  94. // Make sure the overrides passed in are a valid overrides object
  95. const _overrides = Typed.dereference(arg, "overrides");
  96. assertArgument(typeof (_overrides) === "object", "invalid overrides parameter", "overrides", arg);
  97. // Create a shallow copy (we'll deep-ify anything needed during normalizing)
  98. const overrides = copyRequest(_overrides);
  99. assertArgument(overrides.to == null || (allowed || []).indexOf("to") >= 0, "cannot override to", "overrides.to", overrides.to);
  100. assertArgument(overrides.data == null || (allowed || []).indexOf("data") >= 0, "cannot override data", "overrides.data", overrides.data);
  101. // Resolve any from
  102. if (overrides.from) {
  103. overrides.from = overrides.from;
  104. }
  105. return overrides;
  106. }
  107. /**
  108. * @_ignore:
  109. */
  110. export async function resolveArgs(_runner, inputs, args) {
  111. // Recursively descend into args and resolve any addresses
  112. const runner = getRunner(_runner, "resolveName");
  113. const resolver = canResolve(runner) ? runner : null;
  114. return await Promise.all(inputs.map((param, index) => {
  115. return param.walkAsync(args[index], (type, value) => {
  116. value = Typed.dereference(value, type);
  117. if (type === "address") {
  118. return resolveAddress(value, resolver);
  119. }
  120. return value;
  121. });
  122. }));
  123. }
  124. function buildWrappedFallback(contract) {
  125. const populateTransaction = async function (overrides) {
  126. // If an overrides was passed in, copy it and normalize the values
  127. const tx = (await copyOverrides(overrides, ["data"]));
  128. tx.to = await contract.getAddress();
  129. if (tx.from) {
  130. tx.from = await resolveAddress(tx.from, getResolver(contract.runner));
  131. }
  132. const iface = contract.interface;
  133. const noValue = (getBigInt((tx.value || BN_0), "overrides.value") === BN_0);
  134. const noData = ((tx.data || "0x") === "0x");
  135. if (iface.fallback && !iface.fallback.payable && iface.receive && !noData && !noValue) {
  136. assertArgument(false, "cannot send data to receive or send value to non-payable fallback", "overrides", overrides);
  137. }
  138. assertArgument(iface.fallback || noData, "cannot send data to receive-only contract", "overrides.data", tx.data);
  139. // Only allow payable contracts to set non-zero value
  140. const payable = iface.receive || (iface.fallback && iface.fallback.payable);
  141. assertArgument(payable || noValue, "cannot send value to non-payable fallback", "overrides.value", tx.value);
  142. // Only allow fallback contracts to set non-empty data
  143. assertArgument(iface.fallback || noData, "cannot send data to receive-only contract", "overrides.data", tx.data);
  144. return tx;
  145. };
  146. const staticCall = async function (overrides) {
  147. const runner = getRunner(contract.runner, "call");
  148. assert(canCall(runner), "contract runner does not support calling", "UNSUPPORTED_OPERATION", { operation: "call" });
  149. const tx = await populateTransaction(overrides);
  150. try {
  151. return await runner.call(tx);
  152. }
  153. catch (error) {
  154. if (isCallException(error) && error.data) {
  155. throw contract.interface.makeError(error.data, tx);
  156. }
  157. throw error;
  158. }
  159. };
  160. const send = async function (overrides) {
  161. const runner = contract.runner;
  162. assert(canSend(runner), "contract runner does not support sending transactions", "UNSUPPORTED_OPERATION", { operation: "sendTransaction" });
  163. const tx = await runner.sendTransaction(await populateTransaction(overrides));
  164. const provider = getProvider(contract.runner);
  165. // @TODO: the provider can be null; make a custom dummy provider that will throw a
  166. // meaningful error
  167. return new ContractTransactionResponse(contract.interface, provider, tx);
  168. };
  169. const estimateGas = async function (overrides) {
  170. const runner = getRunner(contract.runner, "estimateGas");
  171. assert(canEstimate(runner), "contract runner does not support gas estimation", "UNSUPPORTED_OPERATION", { operation: "estimateGas" });
  172. return await runner.estimateGas(await populateTransaction(overrides));
  173. };
  174. const method = async (overrides) => {
  175. return await send(overrides);
  176. };
  177. defineProperties(method, {
  178. _contract: contract,
  179. estimateGas,
  180. populateTransaction,
  181. send, staticCall
  182. });
  183. return method;
  184. }
  185. function buildWrappedMethod(contract, key) {
  186. const getFragment = function (...args) {
  187. const fragment = contract.interface.getFunction(key, args);
  188. assert(fragment, "no matching fragment", "UNSUPPORTED_OPERATION", {
  189. operation: "fragment",
  190. info: { key, args }
  191. });
  192. return fragment;
  193. };
  194. const populateTransaction = async function (...args) {
  195. const fragment = getFragment(...args);
  196. // If an overrides was passed in, copy it and normalize the values
  197. let overrides = {};
  198. if (fragment.inputs.length + 1 === args.length) {
  199. overrides = await copyOverrides(args.pop());
  200. if (overrides.from) {
  201. overrides.from = await resolveAddress(overrides.from, getResolver(contract.runner));
  202. }
  203. }
  204. if (fragment.inputs.length !== args.length) {
  205. throw new Error("internal error: fragment inputs doesn't match arguments; should not happen");
  206. }
  207. const resolvedArgs = await resolveArgs(contract.runner, fragment.inputs, args);
  208. return Object.assign({}, overrides, await resolveProperties({
  209. to: contract.getAddress(),
  210. data: contract.interface.encodeFunctionData(fragment, resolvedArgs)
  211. }));
  212. };
  213. const staticCall = async function (...args) {
  214. const result = await staticCallResult(...args);
  215. if (result.length === 1) {
  216. return result[0];
  217. }
  218. return result;
  219. };
  220. const send = async function (...args) {
  221. const runner = contract.runner;
  222. assert(canSend(runner), "contract runner does not support sending transactions", "UNSUPPORTED_OPERATION", { operation: "sendTransaction" });
  223. const tx = await runner.sendTransaction(await populateTransaction(...args));
  224. const provider = getProvider(contract.runner);
  225. // @TODO: the provider can be null; make a custom dummy provider that will throw a
  226. // meaningful error
  227. return new ContractTransactionResponse(contract.interface, provider, tx);
  228. };
  229. const estimateGas = async function (...args) {
  230. const runner = getRunner(contract.runner, "estimateGas");
  231. assert(canEstimate(runner), "contract runner does not support gas estimation", "UNSUPPORTED_OPERATION", { operation: "estimateGas" });
  232. return await runner.estimateGas(await populateTransaction(...args));
  233. };
  234. const staticCallResult = async function (...args) {
  235. const runner = getRunner(contract.runner, "call");
  236. assert(canCall(runner), "contract runner does not support calling", "UNSUPPORTED_OPERATION", { operation: "call" });
  237. const tx = await populateTransaction(...args);
  238. let result = "0x";
  239. try {
  240. result = await runner.call(tx);
  241. }
  242. catch (error) {
  243. if (isCallException(error) && error.data) {
  244. throw contract.interface.makeError(error.data, tx);
  245. }
  246. throw error;
  247. }
  248. const fragment = getFragment(...args);
  249. return contract.interface.decodeFunctionResult(fragment, result);
  250. };
  251. const method = async (...args) => {
  252. const fragment = getFragment(...args);
  253. if (fragment.constant) {
  254. return await staticCall(...args);
  255. }
  256. return await send(...args);
  257. };
  258. defineProperties(method, {
  259. name: contract.interface.getFunctionName(key),
  260. _contract: contract, _key: key,
  261. getFragment,
  262. estimateGas,
  263. populateTransaction,
  264. send, staticCall, staticCallResult,
  265. });
  266. // Only works on non-ambiguous keys (refined fragment is always non-ambiguous)
  267. Object.defineProperty(method, "fragment", {
  268. configurable: false,
  269. enumerable: true,
  270. get: () => {
  271. const fragment = contract.interface.getFunction(key);
  272. assert(fragment, "no matching fragment", "UNSUPPORTED_OPERATION", {
  273. operation: "fragment",
  274. info: { key }
  275. });
  276. return fragment;
  277. }
  278. });
  279. return method;
  280. }
  281. function buildWrappedEvent(contract, key) {
  282. const getFragment = function (...args) {
  283. const fragment = contract.interface.getEvent(key, args);
  284. assert(fragment, "no matching fragment", "UNSUPPORTED_OPERATION", {
  285. operation: "fragment",
  286. info: { key, args }
  287. });
  288. return fragment;
  289. };
  290. const method = function (...args) {
  291. return new PreparedTopicFilter(contract, getFragment(...args), args);
  292. };
  293. defineProperties(method, {
  294. name: contract.interface.getEventName(key),
  295. _contract: contract, _key: key,
  296. getFragment
  297. });
  298. // Only works on non-ambiguous keys (refined fragment is always non-ambiguous)
  299. Object.defineProperty(method, "fragment", {
  300. configurable: false,
  301. enumerable: true,
  302. get: () => {
  303. const fragment = contract.interface.getEvent(key);
  304. assert(fragment, "no matching fragment", "UNSUPPORTED_OPERATION", {
  305. operation: "fragment",
  306. info: { key }
  307. });
  308. return fragment;
  309. }
  310. });
  311. return method;
  312. }
  313. // The combination of TypeScrype, Private Fields and Proxies makes
  314. // the world go boom; so we hide variables with some trickery keeping
  315. // a symbol attached to each BaseContract which its sub-class (even
  316. // via a Proxy) can reach and use to look up its internal values.
  317. const internal = Symbol.for("_ethersInternal_contract");
  318. const internalValues = new WeakMap();
  319. function setInternal(contract, values) {
  320. internalValues.set(contract[internal], values);
  321. }
  322. function getInternal(contract) {
  323. return internalValues.get(contract[internal]);
  324. }
  325. function isDeferred(value) {
  326. return (value && typeof (value) === "object" && ("getTopicFilter" in value) &&
  327. (typeof (value.getTopicFilter) === "function") && value.fragment);
  328. }
  329. async function getSubInfo(contract, event) {
  330. let topics;
  331. let fragment = null;
  332. // Convert named events to topicHash and get the fragment for
  333. // events which need deconstructing.
  334. if (Array.isArray(event)) {
  335. const topicHashify = function (name) {
  336. if (isHexString(name, 32)) {
  337. return name;
  338. }
  339. const fragment = contract.interface.getEvent(name);
  340. assertArgument(fragment, "unknown fragment", "name", name);
  341. return fragment.topicHash;
  342. };
  343. // Array of Topics and Names; e.g. `[ "0x1234...89ab", "Transfer(address)" ]`
  344. topics = event.map((e) => {
  345. if (e == null) {
  346. return null;
  347. }
  348. if (Array.isArray(e)) {
  349. return e.map(topicHashify);
  350. }
  351. return topicHashify(e);
  352. });
  353. }
  354. else if (event === "*") {
  355. topics = [null];
  356. }
  357. else if (typeof (event) === "string") {
  358. if (isHexString(event, 32)) {
  359. // Topic Hash
  360. topics = [event];
  361. }
  362. else {
  363. // Name or Signature; e.g. `"Transfer", `"Transfer(address)"`
  364. fragment = contract.interface.getEvent(event);
  365. assertArgument(fragment, "unknown fragment", "event", event);
  366. topics = [fragment.topicHash];
  367. }
  368. }
  369. else if (isDeferred(event)) {
  370. // Deferred Topic Filter; e.g. `contract.filter.Transfer(from)`
  371. topics = await event.getTopicFilter();
  372. }
  373. else if ("fragment" in event) {
  374. // ContractEvent; e.g. `contract.filter.Transfer`
  375. fragment = event.fragment;
  376. topics = [fragment.topicHash];
  377. }
  378. else {
  379. assertArgument(false, "unknown event name", "event", event);
  380. }
  381. // Normalize topics and sort TopicSets
  382. topics = topics.map((t) => {
  383. if (t == null) {
  384. return null;
  385. }
  386. if (Array.isArray(t)) {
  387. const items = Array.from(new Set(t.map((t) => t.toLowerCase())).values());
  388. if (items.length === 1) {
  389. return items[0];
  390. }
  391. items.sort();
  392. return items;
  393. }
  394. return t.toLowerCase();
  395. });
  396. const tag = topics.map((t) => {
  397. if (t == null) {
  398. return "null";
  399. }
  400. if (Array.isArray(t)) {
  401. return t.join("|");
  402. }
  403. return t;
  404. }).join("&");
  405. return { fragment, tag, topics };
  406. }
  407. async function hasSub(contract, event) {
  408. const { subs } = getInternal(contract);
  409. return subs.get((await getSubInfo(contract, event)).tag) || null;
  410. }
  411. async function getSub(contract, operation, event) {
  412. // Make sure our runner can actually subscribe to events
  413. const provider = getProvider(contract.runner);
  414. assert(provider, "contract runner does not support subscribing", "UNSUPPORTED_OPERATION", { operation });
  415. const { fragment, tag, topics } = await getSubInfo(contract, event);
  416. const { addr, subs } = getInternal(contract);
  417. let sub = subs.get(tag);
  418. if (!sub) {
  419. const address = (addr ? addr : contract);
  420. const filter = { address, topics };
  421. const listener = (log) => {
  422. let foundFragment = fragment;
  423. if (foundFragment == null) {
  424. try {
  425. foundFragment = contract.interface.getEvent(log.topics[0]);
  426. }
  427. catch (error) { }
  428. }
  429. // If fragment is null, we do not deconstruct the args to emit
  430. if (foundFragment) {
  431. const _foundFragment = foundFragment;
  432. const args = fragment ? contract.interface.decodeEventLog(fragment, log.data, log.topics) : [];
  433. emit(contract, event, args, (listener) => {
  434. return new ContractEventPayload(contract, listener, event, _foundFragment, log);
  435. });
  436. }
  437. else {
  438. emit(contract, event, [], (listener) => {
  439. return new ContractUnknownEventPayload(contract, listener, event, log);
  440. });
  441. }
  442. };
  443. let starting = [];
  444. const start = () => {
  445. if (starting.length) {
  446. return;
  447. }
  448. starting.push(provider.on(filter, listener));
  449. };
  450. const stop = async () => {
  451. if (starting.length == 0) {
  452. return;
  453. }
  454. let started = starting;
  455. starting = [];
  456. await Promise.all(started);
  457. provider.off(filter, listener);
  458. };
  459. sub = { tag, listeners: [], start, stop };
  460. subs.set(tag, sub);
  461. }
  462. return sub;
  463. }
  464. // We use this to ensure one emit resolves before firing the next to
  465. // ensure correct ordering (note this cannot throw and just adds the
  466. // notice to the event queu using setTimeout).
  467. let lastEmit = Promise.resolve();
  468. async function _emit(contract, event, args, payloadFunc) {
  469. await lastEmit;
  470. const sub = await hasSub(contract, event);
  471. if (!sub) {
  472. return false;
  473. }
  474. const count = sub.listeners.length;
  475. sub.listeners = sub.listeners.filter(({ listener, once }) => {
  476. const passArgs = Array.from(args);
  477. if (payloadFunc) {
  478. passArgs.push(payloadFunc(once ? null : listener));
  479. }
  480. try {
  481. listener.call(contract, ...passArgs);
  482. }
  483. catch (error) { }
  484. return !once;
  485. });
  486. if (sub.listeners.length === 0) {
  487. sub.stop();
  488. getInternal(contract).subs.delete(sub.tag);
  489. }
  490. return (count > 0);
  491. }
  492. async function emit(contract, event, args, payloadFunc) {
  493. try {
  494. await lastEmit;
  495. }
  496. catch (error) { }
  497. const resultPromise = _emit(contract, event, args, payloadFunc);
  498. lastEmit = resultPromise;
  499. return await resultPromise;
  500. }
  501. const passProperties = ["then"];
  502. export class BaseContract {
  503. /**
  504. * The target to connect to.
  505. *
  506. * This can be an address, ENS name or any [[Addressable]], such as
  507. * another contract. To get the resovled address, use the ``getAddress``
  508. * method.
  509. */
  510. target;
  511. /**
  512. * The contract Interface.
  513. */
  514. interface;
  515. /**
  516. * The connected runner. This is generally a [[Provider]] or a
  517. * [[Signer]], which dictates what operations are supported.
  518. *
  519. * For example, a **Contract** connected to a [[Provider]] may
  520. * only execute read-only operations.
  521. */
  522. runner;
  523. /**
  524. * All the Events available on this contract.
  525. */
  526. filters;
  527. /**
  528. * @_ignore:
  529. */
  530. [internal];
  531. /**
  532. * The fallback or receive function if any.
  533. */
  534. fallback;
  535. /**
  536. * Creates a new contract connected to %%target%% with the %%abi%% and
  537. * optionally connected to a %%runner%% to perform operations on behalf
  538. * of.
  539. */
  540. constructor(target, abi, runner, _deployTx) {
  541. assertArgument(typeof (target) === "string" || isAddressable(target), "invalid value for Contract target", "target", target);
  542. if (runner == null) {
  543. runner = null;
  544. }
  545. const iface = Interface.from(abi);
  546. defineProperties(this, { target, runner, interface: iface });
  547. Object.defineProperty(this, internal, { value: {} });
  548. let addrPromise;
  549. let addr = null;
  550. let deployTx = null;
  551. if (_deployTx) {
  552. const provider = getProvider(runner);
  553. // @TODO: the provider can be null; make a custom dummy provider that will throw a
  554. // meaningful error
  555. deployTx = new ContractTransactionResponse(this.interface, provider, _deployTx);
  556. }
  557. let subs = new Map();
  558. // Resolve the target as the address
  559. if (typeof (target) === "string") {
  560. if (isHexString(target)) {
  561. addr = target;
  562. addrPromise = Promise.resolve(target);
  563. }
  564. else {
  565. const resolver = getRunner(runner, "resolveName");
  566. if (!canResolve(resolver)) {
  567. throw makeError("contract runner does not support name resolution", "UNSUPPORTED_OPERATION", {
  568. operation: "resolveName"
  569. });
  570. }
  571. addrPromise = resolver.resolveName(target).then((addr) => {
  572. if (addr == null) {
  573. throw makeError("an ENS name used for a contract target must be correctly configured", "UNCONFIGURED_NAME", {
  574. value: target
  575. });
  576. }
  577. getInternal(this).addr = addr;
  578. return addr;
  579. });
  580. }
  581. }
  582. else {
  583. addrPromise = target.getAddress().then((addr) => {
  584. if (addr == null) {
  585. throw new Error("TODO");
  586. }
  587. getInternal(this).addr = addr;
  588. return addr;
  589. });
  590. }
  591. // Set our private values
  592. setInternal(this, { addrPromise, addr, deployTx, subs });
  593. // Add the event filters
  594. const filters = new Proxy({}, {
  595. get: (target, prop, receiver) => {
  596. // Pass important checks (like `then` for Promise) through
  597. if (typeof (prop) === "symbol" || passProperties.indexOf(prop) >= 0) {
  598. return Reflect.get(target, prop, receiver);
  599. }
  600. try {
  601. return this.getEvent(prop);
  602. }
  603. catch (error) {
  604. if (!isError(error, "INVALID_ARGUMENT") || error.argument !== "key") {
  605. throw error;
  606. }
  607. }
  608. return undefined;
  609. },
  610. has: (target, prop) => {
  611. // Pass important checks (like `then` for Promise) through
  612. if (passProperties.indexOf(prop) >= 0) {
  613. return Reflect.has(target, prop);
  614. }
  615. return Reflect.has(target, prop) || this.interface.hasEvent(String(prop));
  616. }
  617. });
  618. defineProperties(this, { filters });
  619. defineProperties(this, {
  620. fallback: ((iface.receive || iface.fallback) ? (buildWrappedFallback(this)) : null)
  621. });
  622. // Return a Proxy that will respond to functions
  623. return new Proxy(this, {
  624. get: (target, prop, receiver) => {
  625. if (typeof (prop) === "symbol" || prop in target || passProperties.indexOf(prop) >= 0) {
  626. return Reflect.get(target, prop, receiver);
  627. }
  628. // Undefined properties should return undefined
  629. try {
  630. return target.getFunction(prop);
  631. }
  632. catch (error) {
  633. if (!isError(error, "INVALID_ARGUMENT") || error.argument !== "key") {
  634. throw error;
  635. }
  636. }
  637. return undefined;
  638. },
  639. has: (target, prop) => {
  640. if (typeof (prop) === "symbol" || prop in target || passProperties.indexOf(prop) >= 0) {
  641. return Reflect.has(target, prop);
  642. }
  643. return target.interface.hasFunction(prop);
  644. }
  645. });
  646. }
  647. /**
  648. * Return a new Contract instance with the same target and ABI, but
  649. * a different %%runner%%.
  650. */
  651. connect(runner) {
  652. return new BaseContract(this.target, this.interface, runner);
  653. }
  654. /**
  655. * Return a new Contract instance with the same ABI and runner, but
  656. * a different %%target%%.
  657. */
  658. attach(target) {
  659. return new BaseContract(target, this.interface, this.runner);
  660. }
  661. /**
  662. * Return the resolved address of this Contract.
  663. */
  664. async getAddress() { return await getInternal(this).addrPromise; }
  665. /**
  666. * Return the deployed bytecode or null if no bytecode is found.
  667. */
  668. async getDeployedCode() {
  669. const provider = getProvider(this.runner);
  670. assert(provider, "runner does not support .provider", "UNSUPPORTED_OPERATION", { operation: "getDeployedCode" });
  671. const code = await provider.getCode(await this.getAddress());
  672. if (code === "0x") {
  673. return null;
  674. }
  675. return code;
  676. }
  677. /**
  678. * Resolve to this Contract once the bytecode has been deployed, or
  679. * resolve immediately if already deployed.
  680. */
  681. async waitForDeployment() {
  682. // We have the deployement transaction; just use that (throws if deployement fails)
  683. const deployTx = this.deploymentTransaction();
  684. if (deployTx) {
  685. await deployTx.wait();
  686. return this;
  687. }
  688. // Check for code
  689. const code = await this.getDeployedCode();
  690. if (code != null) {
  691. return this;
  692. }
  693. // Make sure we can subscribe to a provider event
  694. const provider = getProvider(this.runner);
  695. assert(provider != null, "contract runner does not support .provider", "UNSUPPORTED_OPERATION", { operation: "waitForDeployment" });
  696. return new Promise((resolve, reject) => {
  697. const checkCode = async () => {
  698. try {
  699. const code = await this.getDeployedCode();
  700. if (code != null) {
  701. return resolve(this);
  702. }
  703. provider.once("block", checkCode);
  704. }
  705. catch (error) {
  706. reject(error);
  707. }
  708. };
  709. checkCode();
  710. });
  711. }
  712. /**
  713. * Return the transaction used to deploy this contract.
  714. *
  715. * This is only available if this instance was returned from a
  716. * [[ContractFactory]].
  717. */
  718. deploymentTransaction() {
  719. return getInternal(this).deployTx;
  720. }
  721. /**
  722. * Return the function for a given name. This is useful when a contract
  723. * method name conflicts with a JavaScript name such as ``prototype`` or
  724. * when using a Contract programatically.
  725. */
  726. getFunction(key) {
  727. if (typeof (key) !== "string") {
  728. key = key.format();
  729. }
  730. const func = buildWrappedMethod(this, key);
  731. return func;
  732. }
  733. /**
  734. * Return the event for a given name. This is useful when a contract
  735. * event name conflicts with a JavaScript name such as ``prototype`` or
  736. * when using a Contract programatically.
  737. */
  738. getEvent(key) {
  739. if (typeof (key) !== "string") {
  740. key = key.format();
  741. }
  742. return buildWrappedEvent(this, key);
  743. }
  744. /**
  745. * @_ignore:
  746. */
  747. async queryTransaction(hash) {
  748. throw new Error("@TODO");
  749. }
  750. /*
  751. // @TODO: this is a non-backwards compatible change, but will be added
  752. // in v7 and in a potential SmartContract class in an upcoming
  753. // v6 release
  754. async getTransactionReceipt(hash: string): Promise<null | ContractTransactionReceipt> {
  755. const provider = getProvider(this.runner);
  756. assert(provider, "contract runner does not have a provider",
  757. "UNSUPPORTED_OPERATION", { operation: "queryTransaction" });
  758. const receipt = await provider.getTransactionReceipt(hash);
  759. if (receipt == null) { return null; }
  760. return new ContractTransactionReceipt(this.interface, provider, receipt);
  761. }
  762. */
  763. /**
  764. * Provide historic access to event data for %%event%% in the range
  765. * %%fromBlock%% (default: ``0``) to %%toBlock%% (default: ``"latest"``)
  766. * inclusive.
  767. */
  768. async queryFilter(event, fromBlock, toBlock) {
  769. if (fromBlock == null) {
  770. fromBlock = 0;
  771. }
  772. if (toBlock == null) {
  773. toBlock = "latest";
  774. }
  775. const { addr, addrPromise } = getInternal(this);
  776. const address = (addr ? addr : (await addrPromise));
  777. const { fragment, topics } = await getSubInfo(this, event);
  778. const filter = { address, topics, fromBlock, toBlock };
  779. const provider = getProvider(this.runner);
  780. assert(provider, "contract runner does not have a provider", "UNSUPPORTED_OPERATION", { operation: "queryFilter" });
  781. return (await provider.getLogs(filter)).map((log) => {
  782. let foundFragment = fragment;
  783. if (foundFragment == null) {
  784. try {
  785. foundFragment = this.interface.getEvent(log.topics[0]);
  786. }
  787. catch (error) { }
  788. }
  789. if (foundFragment) {
  790. try {
  791. return new EventLog(log, this.interface, foundFragment);
  792. }
  793. catch (error) {
  794. return new UndecodedEventLog(log, error);
  795. }
  796. }
  797. return new Log(log, provider);
  798. });
  799. }
  800. /**
  801. * Add an event %%listener%% for the %%event%%.
  802. */
  803. async on(event, listener) {
  804. const sub = await getSub(this, "on", event);
  805. sub.listeners.push({ listener, once: false });
  806. sub.start();
  807. return this;
  808. }
  809. /**
  810. * Add an event %%listener%% for the %%event%%, but remove the listener
  811. * after it is fired once.
  812. */
  813. async once(event, listener) {
  814. const sub = await getSub(this, "once", event);
  815. sub.listeners.push({ listener, once: true });
  816. sub.start();
  817. return this;
  818. }
  819. /**
  820. * Emit an %%event%% calling all listeners with %%args%%.
  821. *
  822. * Resolves to ``true`` if any listeners were called.
  823. */
  824. async emit(event, ...args) {
  825. return await emit(this, event, args, null);
  826. }
  827. /**
  828. * Resolves to the number of listeners of %%event%% or the total number
  829. * of listeners if unspecified.
  830. */
  831. async listenerCount(event) {
  832. if (event) {
  833. const sub = await hasSub(this, event);
  834. if (!sub) {
  835. return 0;
  836. }
  837. return sub.listeners.length;
  838. }
  839. const { subs } = getInternal(this);
  840. let total = 0;
  841. for (const { listeners } of subs.values()) {
  842. total += listeners.length;
  843. }
  844. return total;
  845. }
  846. /**
  847. * Resolves to the listeners subscribed to %%event%% or all listeners
  848. * if unspecified.
  849. */
  850. async listeners(event) {
  851. if (event) {
  852. const sub = await hasSub(this, event);
  853. if (!sub) {
  854. return [];
  855. }
  856. return sub.listeners.map(({ listener }) => listener);
  857. }
  858. const { subs } = getInternal(this);
  859. let result = [];
  860. for (const { listeners } of subs.values()) {
  861. result = result.concat(listeners.map(({ listener }) => listener));
  862. }
  863. return result;
  864. }
  865. /**
  866. * Remove the %%listener%% from the listeners for %%event%% or remove
  867. * all listeners if unspecified.
  868. */
  869. async off(event, listener) {
  870. const sub = await hasSub(this, event);
  871. if (!sub) {
  872. return this;
  873. }
  874. if (listener) {
  875. const index = sub.listeners.map(({ listener }) => listener).indexOf(listener);
  876. if (index >= 0) {
  877. sub.listeners.splice(index, 1);
  878. }
  879. }
  880. if (listener == null || sub.listeners.length === 0) {
  881. sub.stop();
  882. getInternal(this).subs.delete(sub.tag);
  883. }
  884. return this;
  885. }
  886. /**
  887. * Remove all the listeners for %%event%% or remove all listeners if
  888. * unspecified.
  889. */
  890. async removeAllListeners(event) {
  891. if (event) {
  892. const sub = await hasSub(this, event);
  893. if (!sub) {
  894. return this;
  895. }
  896. sub.stop();
  897. getInternal(this).subs.delete(sub.tag);
  898. }
  899. else {
  900. const { subs } = getInternal(this);
  901. for (const { tag, stop } of subs.values()) {
  902. stop();
  903. subs.delete(tag);
  904. }
  905. }
  906. return this;
  907. }
  908. /**
  909. * Alias for [on].
  910. */
  911. async addListener(event, listener) {
  912. return await this.on(event, listener);
  913. }
  914. /**
  915. * Alias for [off].
  916. */
  917. async removeListener(event, listener) {
  918. return await this.off(event, listener);
  919. }
  920. /**
  921. * Create a new Class for the %%abi%%.
  922. */
  923. static buildClass(abi) {
  924. class CustomContract extends BaseContract {
  925. constructor(address, runner = null) {
  926. super(address, abi, runner);
  927. }
  928. }
  929. return CustomContract;
  930. }
  931. ;
  932. /**
  933. * Create a new BaseContract with a specified Interface.
  934. */
  935. static from(target, abi, runner) {
  936. if (runner == null) {
  937. runner = null;
  938. }
  939. const contract = new this(target, abi, runner);
  940. return contract;
  941. }
  942. }
  943. function _ContractBase() {
  944. return BaseContract;
  945. }
  946. /**
  947. * A [[BaseContract]] with no type guards on its methods or events.
  948. */
  949. export class Contract extends _ContractBase() {
  950. }
  951. //# sourceMappingURL=contract.js.map