# Quiz dApp In A Polywrap Wrapper ## [2023-10-03 09:02 CDT] Here's a demo version of a money sharing protocol [Arcoíris](https://github.com/fetsorn/arcoiris), hosted at [link](https://quiz.qualifiedself.org), source at [link](https://github.com/fetsorn/quiz-demo/blob/684c8e36d0f01175a1d15faa4f80b17cd80890f3). In the demo, three actors stake one NFT each, play a quiz, and then divide the bank according to player ratings. The JavaScript code that helps users play a quiz on the blockchain - encoding and sending RPC requests - is good enough for a web prototype. However, JavaScript code won't run in native mobile apps planned for the future, and would have to be ported to Swift and Kotlin. With Polywrap, the library can be implemented in WebAssembly once and executed in the same way in many environments - JavaScript, Swift, Kotlin, Python, Go and Rust. To integrate Polywrap in the demo takes two steps. First, rewrite all smart contract calls from JavaScript to AssemblyScript and build a WebAssembly module using Polywrap CLI. Then, execute WebAssembly in React using the Polywrap JavaScript client. ## [09:16 - 14:27 CDT] Break ## [14:27 CDT] The [tutorial](https://docs.polywrap.io/tutorials/create-wraps/tutorial/project-setup) in Polywrap Docs suggests to bootstrap a new wrapper by running `npx polywrap create wasm assemblyscript <project-name>`. That command creates a project with the latest version of polywrap@0.12.1 and a scaffolding for tests. > **_NOTE:_** Confusingly, at first the tutorial tells to clone [polywrap/demos](https://github.com/polywrap/demos.git), which has a set of sample wrappers with older dependencies. The actual bootstrapping command is cited a bit later in a "TIP" quote block. Let's start by rewriting the existing [integration test](https://github.com/fetsorn/arcoiris/blob/main/test/QuizMC.spec.ts) from the Arcoíris protocol to match the tests from other wrappers. The most recent wrappers in [awesome-polywrap](https://github.com/polywrap/awesome-polywrap#wraps) are for [Uniswap](https://github.com/polywrap/uniswap) and [Gnosis Safe](https://github.com/polywrap/safe-contracts-wrapper/blob/main/packages/safe-contracts-wrapper/src/__tests__/e2e/integration.spec.ts). The code will reside in [fetsorn/arcoiris-pw](https://github.com/fetsorn/arcoiris-pw). The test case plays through a quiz from start to finish - it creates an arcoíris redistribution ceremony, accepts each player's stake, starts a quiz round, simulates commit-reveal of guesses for each player, judges the quiz results and redistributes wealth to the winners. The first call creates a Gathering struct on the arcoíris smart contract. The `createGathering` method is mutable, so it returns a value by emitting an event in logs. The original JavaScript code uses ethers to send a transaction and await on the transaction receipt. ```typescript= const txGathering = await arcoiris.createGathering( token.target, proportional.target, quizMC.target, false ); const receiptGathering = await txGathering.wait(); const gatheringID = receiptGathering.logs[0].topics[1]; ``` Let's rewrite this to call the Polywrap client instead. ```typescript= const { value: { result: gatheringID } } = await client.invoke<App.Gathering>({ uri, method: "createGathering", args: { token, proportional, quizMC, isMutable } }); ``` At this point it's not clear yet how to initialize the client, although sample wrappers seem to have some boilerplate that creates an instance of type ClientConfigBuilder. Let's file that off for later. The original test setup includes ethers calls to an ERC721 contract that mint and approve tokens for each player. There doesn't seem to be a `token` wrapper, and the Uniswap wrapper just implements `approve` among its methods, seemingly breaking separation of concerns. Let's just leave these ethers calls be for now, since they are only required for the test suite. A bit further in, there's a call that impersonates another sender, Alice. It's not yet clear how to do this in Polywrap, perhaps there's some way to change the client config? Let's put it aside for now as well. ```typescript const txContributeAlice = await arcoiris .connect(alice) .contribute(gatheringID, ceremonyID, token.target, tokenAlice); await txContributeAlice.wait(); ``` ```typescript // TODO: connect client to Alice's wallet await client.invoke<App.Ethereum_TxResponse>({ uri, method: "contribute", args: { arcoiris, gatheringID, ceremonyID, token.target, tokenAlice, }, }); ``` Code committed so far is pinned [here](https://github.com/fetsorn/arcoiris-pw/blob/6bec6e6b87a52f1771310c57ee34f570b6203e2d/src/__tests__/e2e/integration.spec.ts). Now it's time to figure out the client initialization and get the test running. The Gnosis Safe wrapper appears to [initialize](https://github.com/polywrap/safe-contracts-wrapper/blob/1b92841e7f7e93d9bd5485559dfed1a6888566d5/packages/safe-managers-wrapper/src/__tests__/e2e/onChainSignatures.spec.ts#L56) a new client with a [custom config](https://github.com/polywrap/safe-contracts-wrapper/blob/1b92841e7f7e93d9bd5485559dfed1a6888566d5/packages/safe-managers-wrapper/src/__tests__/utils.ts#L68) each time it needs to switch signers. Utility functions that do all the necessary legwork seem pretty involved, so let's hope that copypasting them will be enough. > **_NOTE:_** Apparently, there are two ways to call the Polywrap client. The Safe wrapper calls `App.Module.methodName(args, client, uri)`, while the Uniswap wrapper uses `client.invoke(uri, methodName, args)`. ## [15:42 CDT] It's becoming clear that the old code for token mints and approvals won't work anymore, at least not out of the box, because of the broken dependency on hardhat. Test setup for the Gnosis Safe wrapper deploys contracts in two different ways. Sometimes it invokes the wrapper methods, which call on the ethers-rs wrapper. Other times it uses ethers.js with signers' private keys and the `ETH_ENS_IPFS_MODULE_CONSTANTS.ethereumProvider` constant exposed by Polywrap CLI. Let's follow the latter example. ```typescript import { ethers, Signer, Wallet } from "ethers"; import { ETH_ENS_IPFS_MODULE_CONSTANTS } from "@polywrap/cli-js"; import { abi as ArcoirisABI, bytecode as ArcoirisBytecode, } from "arcoiris/artifacts/Arcoiris.sol/Arcoiris.json"; import { Arcoiris } from "arcoiris/typechain-types"; import { pkPoller, pkAlice, pkBob } from "./constants"; export const setupContractNetworks = async ( client: PolywrapClient ): Promise<any> => { const provider = new ethers.providers.JsonRpcProvider( ETH_ENS_IPFS_MODULE_CONSTANTS.ethereumProvider ); const poller = new Wallet(pkPoller, provider); const alice = new Wallet(pkAlice, provider); // <...> const arcoirisFactory = new ethers.ContractFactory( ArcoirisABI, ArcoirisBytecode, poller ); const arcoiris = (await arcoirisFactory.deploy()) as Arcoiris; return { poller, alice, bob, token, arcoiris }; }; ``` ```typescript const { poller, alice, bob, token, arcoiris } = await setupContractNetworks( new PolywrapClient(getClientConfig()) ); const clientPoller = new PolywrapClient( getClientConfig({ signer: poller.address }) ); // <...> const txMintAlice = await token.mint(alice.address); const receiptMintAlice = await txMintAlice.wait(); const tokenAlice = receiptMintAlice.logs[0].topics[3]; const clientAlice = new PolywrapClient( getClientConfig({ signer: alice.address }) ); await clientAlice.invoke<App.Ethereum_TxResponse>({ uri, method: "contribute", args: { arcoiris, gatheringID, ceremonyID, token.target, tokenAlice, }, }); ``` ## [16:43 CDT] Test setup seems ready, but it won't build until the wrapper has a GraphQL schema describing its interface. Polywrap requires that schema to run code generation and determine static types for wrapper methods. Code committed so far is pinned [here](https://github.com/fetsorn/arcoiris-pw/blob/e666727bcd8726e806c7f8331193c7d7a716a5f0/src/__tests__/e2e/integration.spec.ts). ## [2023-10-03 16:43 CDT - 2023-10-05 07:49 CDT] Break ## [07:49 CDT] Before writing the wrapper interface, let's dry run Polywrap code generation to see what errors in types and environment need fixing. Tests in Safe and Uniswap wrappers run docker to build WebAssembly and to spin up a ganache testing network. Both steps work can probably work locally so docker is unnecessary overhead. Let's build the wrapper locally by adding a flag to `npx polywrap build -s local` and run a local ethereum network with `npx hardhat node`. The polywrap client depends on ethers v5, and the test code is already at v6, so passing the Signer to the client config fails type checking, but apparently there's an option to pass an account address instead. It is unclear how the client signs transaction without a private key, but passing only an account address works. The constant for testing environments is probably aware of the private keys for the testing network. ## [11:20] Let's add the graphql schema and empty method implementations to build the wrapper. ## [12:35] > _**NOTE**_: the errors coming from polywrap wraper are large so it's better to pipe them through `less` Arcoiris has an outstanding refactor to merge `commitCorrect` and `commitGuess` methods, but it's outside the scope of this integration so calling two methods separately will have to do for now. Code committed so far is pinned [here](https://github.com/fetsorn/arcoiris-pw/blob/8dc2108c00222ebe73f0ee440183e726fddb7629/src/__tests__/e2e/integration.spec.ts). ## [12:52] Now that the test builds, let's add wrapper implementation. > _**NOTE**_: Every time there's a need to figure out a type for the ethers wrapper, it can be checked in `src/__tests__/types/wrap/types.ts`. `createGathering` wrapper method needs to call on ethers wrapper, send an ethereum transaction and sift through logs to find return values - Safe wrapper already has similar code. ```typescript const tx = Ethers_Module.callContractMethodAndWait({ address: args.arcoiris, method: "function createGathering(address collection, address redistribution, address mc, bool isMutable) external returns (uint256 gatheringID)", args: [ args.collection, args.redistribution, args.mc, args.isMutable.toString(), ], }).unwrap(); ``` However, the call to `Ethers_Module.callContractMethodAndWait` returns an error. The error happens in the Rust ethers wrapper, which is called by the JavaScript client from inside the arcoiris AssemblyScript wrapper executed by the JavaScript client - and it propagates down the stack yielding a hefty error object that spans 77 lines in the console. The important part is ``` called `Option::unwrap()` on a `None` value ``` Apparently, somewhere in the ethers-rs code a call to the blockchain returned `Option:None` instead of `Result:Ok`. Since debugging this seems a bit too involved, let's try another call, awaiting on the transaction separately. ```typescript const response = Ethers_Module.callContractMethod({ address: args.arcoiris, method: "function createGathering(address collection, address redistribution, address mc, bool isMutable) external returns (uint256 gatheringID)", args: [ args.collection, args.redistribution, args.mc, args.isMutable.toString(), ], }).unwrap(); const tx = Ethers_Module.awaitTransaction({ txHash: response.hash, confirmations: 1, }).unwrap(); ``` Yields the same error. ``` called `Option::unwrap()` on a `None` value ``` Perhaps using the javascript version of ethers wrapper helps? Let's replace the ENS import address in the wrapper schema. ```graphql # import { Module, TxResponse, TxReceipt } into Ethers from "wrapscan.io/polywrap/ethers@1.0" # import { Module, TxResponse, TxReceipt } into Ethers from "wrap://ens/ethers.wraps.eth:0.1.0" ``` Now the error object spans 111 lines in the log, but inside it there's an RPC error message `unknown account 0x90f8bf6a479f320ead074411a4b0e7944ea8c9c1`. That is an issue with hardhat setup, fixed by changing accounts private keys to those unlocked by hardhat at the start. But running the test again yields a 1894 line error object, the client cannot find local ens and ipfs nodes - those require Docker. There is probably a way to specify a public IPFS and ENS endpoint, but it is not clear where the documentation for that is. Perhaps there's a way to import the ethers wrapper without ens? There's an option specify an http endpoint to ipfs storage. The IPFS CIDs can be found at https://app.ens.domains/wraps.eth and resolved at `https://ipfs.io/ipfs/{cid}`. This works well in the client configuration, where the config builder downloads an ethereum-providers `wrap.info` from `https://ipfs.io/ipfs/QmPeHGkHn9Fwo1Drh39SGNfW3bBNSdAg14hcHYwux2oWrc` . However it doesn't work with the ethers wrapper in graphql schema - the client only downloads `wrap.info` and fails to download `wrap.wasm` ```graphql #import { Module, TxResponse, TxReceipt } into Ethers from "t " ``` ``` reason: 'WasmWrapper: Wrapper does not contain a wasm module. WrapError: __wrap_abort: Error during HTTP request: WrapError: connect ECONNREFUSED ::1:80\n' + code: 51 WRAPPER INVOKE ABORTED uri: wrap://wrapscan.io/polywrap/http@1.0 method: get args: { "url": "ipfs.io/ipfs/QmVkEd5KUkxaDLWkvxBw2dXpEJ43SkohxekFPrtDHqruhS/wrap.wasm", ``` let's just download the ethers wrap.wasm and ship it alongside the package for now. There's probably a way to redirect ens to fs later. ```graphql #import { Module, TxResponse, TxReceipt } into Ethers from "wrap://fs/./ethers" ``` ## [15:07 CDT] Now the createGathering implementation is working. Let's implement the rest of the methods. ```typescript createGathering(args: Args_createGathering): BigInt { const tx = Ethers_Module.callContractMethodAndWait({ address: args.arcoiris, method: "function createGathering(address collection, address redistribution, address mc, bool isMutable) external returns (uint256 gatheringID)", args: [ args.collection, args.redistribution, args.mc, args.isMutable.toString(), ], }).unwrap(); const createGatheringEvent = "0x7278386a55533b695ad284d518708f0ebf2b7c029ca31a5c744110eab102daf9"; const index = tx.logs.findIndex( (log: Ethers_Log) => log.topics[0] == createGatheringEvent, ); if (index == -1) { throw new Error( "Couldn't fetch address from event logs from transaction " + tx.transactionHash, ); } const gatheringID = tx.logs[index].topics[1]; return BigInt.from(gatheringID); } ``` ## [2023-10-05 15:07 CDT - 2023-10-06 06:49 CDT] Now that it's clear how to call ethers from the wrapper, the rest of the methods are boilerplate to write. > _**NOTE**_: there is always gonna be a duplication of tests between smart contract development and wrapper development. Contracts are usually tested in javascript, or solidity. Fortunately arcoíris has both jest and foundry tests - if it only had the ones in Solidity, it would be harder to copy tests. > _**NOTE**_: calling toString() on every argument in Ethers_Module.callContractMethodAndWait seems like a failure of type generation. perhaps there's another method to encode? ## [13:37 CDT] The original test hashed arrays of strings with ethers and passed ethers byte objects to the method call. The same byte objects can't be passed to the ethers wrapper, and in any case that code should be inside the arcoris wrapper as part of sdk. it appears from the Uniswap wrapper that hashing functions are in ethers-utils wrapper. In contrast to the ethers wrapper that is now imported from the local filesystem, ethers-utils URI works out the gate `wrapscan.io/polywrap/ethers-utils@1.0`, apparently because it does not require ens or ipfs resolution. Original javascript code relied on `id`, `toUtf8Bytes`, `concat`, `keccak256` to sanitize, hash and encode the arguments, and ethers-utils only provides the `keccak256`, so here's hoping AssemblyScript's `concat` and `toString` are enough. If not, perhaps encoding function params and sending a raw transaction instead of `callContractMethodAndWait` works. > _**NOTE**_: ethers-utils methods take an arguments object, so instead of `EthersUtils_Module.keccak256(data)` the call should be `EthersUtils_Module.keccak256({ value: data })`. The interface for each method can be found in the [wrapper schema](https://www.wrapscan.io/wrap/wrapscan.io%2Fpolywrap%2Fethers%401.1.1/schema) or in `src/wrap/imported/EthersUtils_Module`. ## [17:32 CDT] > _**NOTE:**_ it is unfortunate that calls to polywrap client don't return a rejected promise, but some sort of error object. What prevents invoke from returning a Promise that rejects on "result.ok == false"? Perhaps the polymorphism of the invoke function? Ethers wrapper doesn't provide functions for converting strings to bytes. And using ethers alongside polywrap would defeat the purpose of integration. It might be easier to rewrite the arcoiris contract to receive strings instead of bytes but that would also defeat the purpose of this research. Let's implement string conversion in the wrapper. ## [2023-10-06 17:32 CDT - 2023-10-08 06:05 CDT] Break ## [2023-10-08 06:05 CDT] Since ethers-utils wrapper doesn't provide utilities for hex strings, let's use the `as-hex` package. So this typescript code ```typescript import { ethers } from "ethers"; function hashValue(saltID: string) { return (value: string) => { const salted = ethers.concat([ethers.toUtf8Bytes(value), saltID]); const hash = ethers.keccak256(salted); return hash; }; } ``` becomes ```typescript import { EthersUtils_Module } from "./wrap"; import { encode } from "as-hex"; function hashValues(values: string[], saltID: string): string[] { var hashes = new Array<string>(0); for (let i = 0, l = values.length; i < l; ++i) { const salted = "0x" + encode(values[i]) + saltID.substring(2); const hash = EthersUtils_Module.keccak256({ value: salted, }).unwrap(); hashes.push(hash); } return hashes; } ``` And the test passes Code committed so far is pinned [here](https://github.com/fetsorn/arcoiris-pw/blob/ea9464c874b94de76a2542c58d9ad344db8e9a51/src). Now let's integrate the wrapper with the React [app](https://github.com/fetsorn/quiz-demo). Right now the demo app imports `ethers` and `arcoiris` to interact with contracts. To replace these dependencies with `polywrap` would require adding ERC721 functions to the wrapper as well. These can later be moved to a separate wrapper for ERC token standards. Let's draft what the demo code would be like to see if any other methods are missing from the wrapper API. Example integration will be based on the Uniswap wrapper [demo](https://github.com/polywrap/uniswap/tree/main/demos/v3-walkthrough). > _**NOTE:**_ The Uniswap wrapper demo initializes `PolywrapProvder` with `DefaultBundle` from `@polywrap/client-js` which has since been deprecated. Apparently now `PolywrapProvider` can be created without props. Code committed so far is pinned [here](https://github.com/fetsorn/quiz-demo/blob/e2c3803d0f7ac3141c1fecb377485d1041a2c8b7/src/root.jsx). ## [8:06 AM] Let's add the missing methods to the GraphQL schema. Before implementing them, however, let's complete setting up React and initializing the Polywrap client, which might show what other changes to the wrapper are required. Code committed so far is pinned [here](https://github.com/fetsorn/arcoiris-pw/blob/6701acad2329f898299ca579d01fe2d7c168fdff/src). It is not clear how to connect the polywrap client to a custom wallet private key, alternative to `ethers.Contract.connect(signer)`. In the tests that was done by passing an account address to the client setup yanked from the Safe wrapper, but that entire setup depends on `@polywrap/cli-js` and likely won't work in the browser. Perhaps there's a method in `@polywrap/react`? [Docs](https://docs.polywrap.io/reference/clients/js/libraries/react) for `@polywrap/react` are down. [React Integration](https://docs.polywrap.io/tutorials/use-wraps/react-integration) tutorial doesn't mention custom wallets either. [Configure Client](https://docs.polywrap.io/tutorials/use-wraps/configure-client) tutorial has a broken [link](https://github.com/polywrap/javascript-client/tree/origin-dev/packages/client-config-builder) to `@polywrap/client-config-builder-js`. The [readme](https://github.com/polywrap/javascript-client/tree/origin-dev/packages/config-builder) for `@polywrap/client-config-builder-js` doesn't mention a custom wallet, but at the bottom has a [link](https://www.npmjs.com/package/@polywrap/web3-config-bundle-js) to `web3 bundle` npm package. The readme for that bundle contains an implementation of the bundle instead of docs. The [source](https://github.com/polywrap/javascript-client/blob/3afd306be93d49d9000a3236093950a63b9931d9/packages/config-bundles/web3/src/index.ts#L6) of the bundle contains a line `$start: bundle`, so apparently the package readme is auto-generated from the source code. The web3 bundle itself depends on the `sys` bundle. Both bundles are string maps of interface `{ uri, package, implements, redirectFrom }`, which seems to be the type of 'package' in the config builder. Perhaps custom wallet can be connected by calling `.addPackage` on the config builder with a custom package from `@polywrap/ethereum-wallet-js` copied from the `web3 bundle`, with a custom provider which knows the account private key. ```typescript ethereumWallet: { uri: "plugin/ethereum-wallet@1.0", package: EthWallet.plugin({ connections: new EthWallet.Connections({ networks: { mainnet: new EthWallet.Connection({ provider: "https://mainnet.infura.io/v3/b00b2c2cc09c487685e9fb061256d6a6", }), goerli: new EthWallet.Connection({ provider: "https://goerli.infura.io/v3/b00b2c2cc09c487685e9fb061256d6a6", }), }, }), }) as IWrapPackage, implements: ["wrapscan.io/polywrap/ethereum-wallet@1.0"], redirectFrom: ["wrapscan.io/polywrap/ethereum-wallet@1.0"], }, ``` > _**NOTE:**_ It is challenging to create a mental map of how this would work, but let's assume it's ultimately the same `ethers.connect()` under the hood. How would a custom ethereum wallet provider be created? The [readme](https://www.npmjs.com/package/@polywrap/ethereum-wallet-js) for `@polywrap/ethereum-wallet-js` at npm registry doesn't link to source code, but has a broken link to `wrapscan.io/polywrap/ethereum-wallet@1.0` which leads to `https://www.npmjs.com/interface/`. The URL https://wrapscan.io/polywrap/ethereum-wallet@1.0 itself returns 404. The [source code](https://github.com/polywrap/ethereum-wallet/tree/main/implementations/js) can be found on GitHub, and again it depends on ethers v5 so there'll have to be some compatibility work done. The cognitive difficulty at this point is too high to maintain a train of thought, so let's start over. React demo calls the main Metamask account and three mock accounts initiated from private keys. While the main account should work with `usePolywrapClient`, each mock requires a separate instance of `PolywrapClient`, initialized with a config that contains the account's Signer. That is probably done with something like ```javascript import { ethereumWalletPlugin, Connections, Connection } from '@polywrap/ethereum-wallet-js'; import { Wallet } from 'ethers'; import { privateKey } from './constants.js'; const clientMock = new PolywrapClient( new PolywrapClientConfigBuilder() .addDefaults() .addPackage({ uri: "plugin/ethereum-wallet", package: ethereumWalletPlugin({ connections: new Connections({ networks: { mainnet: new Connection({ provider: window.ethereum, signer: new Wallet(privateKey) }), }, }), }), implements: ["wrapscan.io/polywrap/ethereum-wallet@1.0"], redirectFrom: ["wrapscan.io/polywrap/ethereum-wallet@1.0"], }) .build() ); ``` It is not clear if `window.ethereum` would work here, but theoretically every provider should implement EIP-1193. It is not clear how and whether the client will choose to use the `mainnet` field of the `networks` map. > _**NOTE**:_ Mock accounts require `ethers.Wallet`, but don't work with the latest ethers v6 because ethereum-wallet depends on ethers v5. It would be better if Connection accepted raw private keys and initiated wallets without requiring the user to import a matching version of ethers. The mock accounts initate well with `signer: ethers.Wallet(privateKey)`, but it would be better ## [09:06 CDT - 11:00 CDT] Break ## [11:00 AM] The easiest way to publish the wrapper is probably to host it statically at https://static.qualifiedself.org/arcoiris-pw and specify the https URI in the client. After adding buffer polyfill and a CSP connect-src policy for infura the app builds and fails at runtime with with "Invalid hook call. You might have mismatching versions of React and the renderer." Indeed, the demo uses `react@18.2.0` and `polywrap/react` depends on `react@16.9.0`. Let's assume `polywrap/react` is not necessary and replace it with `polywrap/client-js`. Now the config builder fails with `builder.addPackage is not a function`. Ah, the code yanked from Uniswap depended on earlier version of the API. The new API docs recommend `setPackage` and no `implements` or `redirectsFrom`. > _**NOTE:**_ The cognitive difficulty of the config builder documentation is astounding. Now the code fails at the last piece of code that still depends on ethers - `signer.address`. This line used to get address of ethers Signer, but now that Polywrap client replaced ethers, signer probably has to be replaced with something like `client.address`. The [readme](https://www.npmjs.com/package/@polywrap/client-js) of `@polywrap/client-js` has no docs, it leads to the package [repository](https://github.com/polywrap/javascript-client) `polywrap/client-javascript`, which has a link "Documentation" that leads to https://docs.polywrap.io which has a link `Clients` that leads back to `polywrap/client-javascript` repository. When logging the client object to see what methods the client implements, it has nothing like "address". There is probably some method that can be called with `invoke` on the `ethereum-wallet` package. Indeed, package's GraphQL [schema](https://github.com/polywrap/ethereum-wallet/blob/890402a501e2df41ea05268e544db47fe3d37281/interface/polywrap.graphql#L29C3-L29C16) has a `signerAddress(connection: Connection): String`. However, it returns `{ ok: true, value: null }`. But the provider initialization clearly worked because CSP asked for a bunch of calls to ipfs and wrapscan. Ah, the code is missing `await window.ethereum.request({method: 'eth_requestAccounts'})`. Let's check that a call to the arcoiris wrapper is working. It will probably fail because the filesystem uri in the wrapper imports. Yeap, it fails, but doesn't complain about error resolution and instead returns `Error.captureStackTrace is not a function`. `captureStackTrace` is specific to V8, and it fails because the tests are done in Firefox, which uses `error.stack` instead. Apparently Polywrap does not support Firefox yet. A Chromium browser properly returns `Unable to find URI wrap://fs/./wrap_modules/ethers`. ## [12:00 CDT - 14:00 CDT] break ## [15:16 CDT] client fails with the same `IPFS method 'cat' failed. WrapError: Request failed with status code 500`. The client tried to find a `wrap.wasm` at `https://ipfs.io/ipfs/QmPeHGkHn9Fwo1Drh39SGNfW3bBNSdAg14hcHYwux2oWrc/` which only had a `wrap.info`. It took a while to realize that happened because some wrapper imported `wrap://ens/wraps.eth:ethereum-provider@2.0.0` which required redirect to the ethereum wallet plugin set at `plugin/ethereum-wallet`. Reading the error message that has an `innerError` that has a `reason` and figuring out the cause of that error is challenging. Now that Polywrap works well in React, let's add the missing getters to the wrapper - and the integration is complete. Here's pinned source code for the [wrapper](https://github.com/fetsorn/arcoiris-pw/commit/86a1a267b715a93d96e1f55e3e6a1159d13ca78f) and the [demo](https://github.com/fetsorn/quiz-demo/commit/6543f98178735522195837c6d55d0b976df7d068). # notes Polywrap should be more direct that it solves problems related to wasm packaging - async, serialization, caching, package management, subinvocation, package management, wasi support, types, cross-language api, wasm executor compatibility. ## async i believe the user should have a very explicit and transparent library for this. you import asyncify, you asyncify all your wasm programs. you know about it. cause otherwise the user is in the dark and when something breaks they will find it hard to debug. Hiding asyncify in client is good for abstraction, but mentioning in the docs it is good for clear positioning of why the client is needed in the first place. Ideally each problem space would have separate libraries, with the client only providing sane default way of tying them together. Like, I want to asyncify imports for any wasm, or mspackify exports ## msgpackify I believe there should be a separate library, ## caching I'd like to know Polywrap story on caching but haven't reached out to the codebase to study. Is wasm bytecode cached? Are compilations? Are fetches? Are execution results memoized? Polywrap should cache wasm in wasm_modules during test. Polywrap should cache resolution stacks as well. Testing should work airgapped after caching unless user explicitly makes http calls. ## subinvocation I'd like subinvokation to be a standard with docs. What import function is called? What interface it has? How do I shim subinvoke on my own? ## types I want ./wrap stuff to be optional If ./wrap is like typechain, how do I go around it and invoke as if it is optional? Do I have to provide my own return types? Is that sometimes faster and leaner than ./wrap? ## package management I want the package management story to be explicit. Are ens records immutable? Where is package metadata? Mutable links to .wasm files are not sufficient I didn't know if ethers wrapper was in rust, or a plugin, or what, and could not debug it, had to hope another ens name works ## wasi Polywrap should shim wasi imports for reactor modules ## compatibility Polywrap should have some compatibility story with wasm reactor modules At least be able to run a module 'add(a,b) { return a+b )' Wrap modules should have some minimal compatibility with other executors of reactor modules Like, at least compile a module 'add(a,b) { return a+b )' that can run without client and returns msgpack data So that only msgpack shim is needed to execute a wrap module ## common API the same client.invoke(url, method) in rust, python, js, go and everywhere is great but could be improved to mirror ethers. instantiate wrappers with function interfaces instead of invoke(uri, method, args) ```typescript const arcoiris = new Wrapper(client, uri); const quizID = await arcoiris.createQuiz({ quizMC, ceremonyID }); ```