Execute & Broadcast

Note: "safe" is sometimes used in method names or param names for the Avocado RPC, "safe" there refers to an "avocado".

Execution differs between Avocado Personal (1 signer, index 0) and Avocado Multisigs.

Personal (index == 0 && requiredSigners == 1)

index 0 is treated as AvocadoPpersonal on https://avocado.instadapp.io/

const chainId = (await polygonProvider.getNetwork()).chainId; // e.g. when executing on Polygon provider

const txHash = await avocadoProvider.send("txn_broadcast", [
  {
    signatures: [
      {
        signature: "0xsignature", // signature as built in previous step
        signer: "0xsigner", // signer address that signed the signature
      },
    ],
    message: txPayload, // transaction payload as built in previous step
    owner: "avocado owner", // avocado owner EOA address
    safe: "avocado address", // avocado address
    index: "0",
    targetChainId: chainId,
    executionSignature: undefined, // only required when index > 0 || signatures.length > 1
  },
]);
Multisig (index > 0 || signatures.length > 1)
Attention: executionSignature might be subject to later changes, it is not final yet.
const executionTypes = {
  Cast: [
    { name: "params", type: "CastParams" },
    { name: "forwardParams", type: "CastForwardParams" },
    { name: "signatures", type: "SignatureParams[]" },
  ],
  CastParams: [
    { name: "actions", type: "Action[]" },
    { name: "id", type: "uint256" },
    { name: "avoNonce", type: "int256" },
    { name: "salt", type: "bytes32" },
    { name: "source", type: "address" },
    { name: "metadata", type: "bytes" },
  ],
  Action: [
    { name: "target", type: "address" },
    { name: "data", type: "bytes" },
    { name: "value", type: "uint256" },
    { name: "operation", type: "uint256" },
  ],
  CastForwardParams: [
    { name: "gas", type: "uint256" },
    { name: "gasPrice", type: "uint256" },
    { name: "validAfter", type: "uint256" },
    { name: "validUntil", type: "uint256" },
    { name: "value", type: "uint256" },
  ],
  SignatureParams: [
    { name: "signature", type: "bytes" },
    { name: "signer", type: "address" },
  ],
};

const avocadoRPCChainId = "634";
const chainId = (await polygonProvider.getNetwork()).chainId; // e.g. when executing later on Polygon

const domain = {
  name: domainName, // see previous steps
  version: domainVersion, // see previous steps
  chainId: avocadoRPCChainId,
  verifyingContract: avocadoAddress, // see previous steps
  salt: ethers.utils.solidityKeccak256(["uint256"], [chainId]), // salt is set to actual chain id where execution happens
};

// the execution signature is used to verify that given enough valid signatures have been collected,
// one of the allowed signers is triggering the actual execution of the signed actions
const avoSigner = provider.getSigner();
const executionSignature = await avoSigner._signTypedData(domain, executionTypes, txPayload);

const txHash = await avocadoProvider.send("txn_broadcast", [
  {
    signatures: [
      {
        signature: "0xsignature1", // signature 1 as built in previous step
        signer: "0xsigner1", // signer address that signed the signature 1
      },
      {
        signature: "0xsignature2", // signature 2 as built in previous step by some other allowed signer
        signer: "0xsigner2", // signer address that signed the signature 2
      },
    ],
    message: txPayload, // transaction payload as built in previous step
    owner: "avocado owner", // avocado owner EOA address
    safe: "avocado address", // avocado address
    index: "0",
    targetChainId: chainId,
    executionSignature,
  },
]);

See the next section for more advanced use-cases!