Bladeren bron

first commit

ljoaquim 1 maand geleden
commit
73048ab69e

+ 5 - 0
.env.example

@@ -0,0 +1,5 @@
+EASY_COIN_ADDR=0x0D674e9384220bf9146fccD70E422B0480CC0a12
+EASY_TOKEN_ADDR=0xA325731B277424f4157B734729C733c46591bfE6
+EASY_ADMIM_PUBLIC_KEY=
+EASY_ADMIM_PRIVATE_KEY=
+POLYGON_RPC_URL=

+ 22 - 0
.gitignore

@@ -0,0 +1,22 @@
+# Node modules
+/node_modules
+
+# Compilation output
+/dist
+
+# pnpm deploy output
+/bundle
+
+# Hardhat Build Artifacts
+/artifacts
+
+# Hardhat compilation (v2) support directory
+/cache
+
+# Typechain output
+/types
+
+# Hardhat coverage reports
+/coverage
+
+.env

+ 35 - 0
README.md

@@ -0,0 +1,35 @@
+# easycli - Compilação rápida
+
+Este repositório contém o CLI em Go (easycli) para interagir com os contratos EasyBRL (ERC20) e EasyToken (ERC721).
+
+## Requisitos
+
+- Go 1.21+
+- .env com pelo menos:
+  - `RPC_URL` (ou `POLYGON_RPC_URL`/`AMOY_RPC_URL`)
+  - `PRIVATE_KEY` (ou `EASY_ADMIN_PRIVATE_KEY`/`EASY_ADMIM_PRIVATE_KEY`)
+  - `EASY_COIN_ADDR` e `EASY_TOKEN_ADDR`
+
+## Build e uso básico
+
+Na pasta `sdk/`:
+
+```bash
+go mod tidy
+go build -o easycli .
+./easycli --help
+```
+
+Exemplos rápidos:
+
+```bash
+./easycli --rpc "$RPC_URL" --coin-addr "$EASY_COIN_ADDR" coin info
+./easycli --rpc "$RPC_URL" --token-addr "$EASY_TOKEN_ADDR" token info
+./easycli polygon create-new-address
+```
+
+Documentação completa (todas as opções, exemplos e saídas esperadas):
+
+- doc/EASYCLI.md
+
+Observação: se o .env estiver com fim de linha Windows (CRLF), algumas chaves podem falhar com erro `invalid hex character '\r'`. Troque o arquivo para LF.

+ 72 - 0
contracts/EasyBRLStable.sol

@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.20;
+
+import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
+import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
+import "@openzeppelin/contracts/access/AccessControl.sol";
+import "@openzeppelin/contracts/utils/Pausable.sol";
+
+contract EasyBRLStable is ERC20, ERC20Burnable, AccessControl, Pausable {
+    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
+    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
+    bytes32 public constant COMPLIANCE_ROLE = keccak256("COMPLIANCE_ROLE");
+
+    mapping(address => bool) private _blacklist;
+
+    event BlacklistUpdated(address indexed account, bool isBlacklisted);
+
+    constructor(address admin, string memory name_, string memory symbol_) ERC20(name_, symbol_) {
+        _grantRole(DEFAULT_ADMIN_ROLE, admin);
+        _grantRole(PAUSER_ROLE, admin);
+        _grantRole(MINTER_ROLE, admin);
+        _grantRole(COMPLIANCE_ROLE, admin);
+    }
+
+    function decimals() public pure override returns (uint8) {
+        return 6;
+    }
+
+    function pause() external onlyRole(PAUSER_ROLE) {
+        _pause();
+    }
+
+    function unpause() external onlyRole(PAUSER_ROLE) {
+        _unpause();
+    }
+
+    function setBlacklist(address account, bool status) external onlyRole(COMPLIANCE_ROLE) {
+        _blacklist[account] = status;
+        emit BlacklistUpdated(account, status);
+    }
+
+    function isBlacklisted(address account) external view returns (bool) {
+        return _blacklist[account];
+    }
+
+    function mint(address to, uint256 amount) external onlyRole(DEFAULT_ADMIN_ROLE) {
+        _mint(to, amount);
+    }
+
+    function burn(uint256 value) public override onlyRole(DEFAULT_ADMIN_ROLE) {
+        super.burn(value);
+    }
+
+    function burnFrom(address account, uint256 value) public override onlyRole(DEFAULT_ADMIN_ROLE) {
+        super.burnFrom(account, value);
+    }
+
+    function _update(address from, address to, uint256 value) internal override whenNotPaused {
+        if (from != address(0)) {
+            require(!_blacklist[from], "Blacklisted");
+        }
+        if (to != address(0)) {
+            require(!_blacklist[to], "Blacklisted");
+        }
+        require(!_blacklist[_msgSender()], "Blacklisted");
+        super._update(from, to, value);
+    }
+
+    function grantComplianceRole(address account) external onlyRole(DEFAULT_ADMIN_ROLE) {
+        _grantRole(COMPLIANCE_ROLE, account);
+    }
+}

+ 134 - 0
contracts/EasyTokenDocument.sol

@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.20;
+
+import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
+import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
+import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Pausable.sol";
+import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
+import "@openzeppelin/contracts/access/AccessControl.sol";
+
+contract EasyTokenDocument is ERC721URIStorage, ERC721Burnable, ERC721Pausable, AccessControl {
+    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
+    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
+    bytes32 public constant COMPLIANCE_ROLE = keccak256("COMPLIANCE_ROLE");
+    bytes32 public constant METADATA_ROLE = keccak256("METADATA_ROLE");
+    bytes32 public constant APPRAISER_ROLE = keccak256("APPRAISER_ROLE");
+
+    mapping(address => bool) private _blacklist;
+    mapping(uint256 => bytes32) private _documentHash;
+    mapping(uint256 => uint256) private _appraisalValue;
+
+    uint256 private _tokenIdCounter;
+
+    event BlacklistUpdated(address indexed account, bool isBlacklisted);
+    event DocumentMinted(uint256 indexed tokenId, address indexed to, string uri, bytes32 documentHash, uint256 appraisalValue);
+    event AppraisalUpdated(uint256 indexed tokenId, uint256 oldValue, uint256 newValue);
+    event TokenURIUpdated(uint256 indexed tokenId, string uri);
+
+    constructor(address admin, string memory name_, string memory symbol_) ERC721(name_, symbol_) {
+        _grantRole(DEFAULT_ADMIN_ROLE, admin);
+        _grantRole(PAUSER_ROLE, admin);
+        _grantRole(MINTER_ROLE, admin);
+        _grantRole(METADATA_ROLE, admin);
+        _grantRole(APPRAISER_ROLE, admin);
+        _grantRole(COMPLIANCE_ROLE, admin);
+    }
+
+    function valueDecimals() public pure returns (uint8) {
+        return 6;
+    }
+
+    function pause() external onlyRole(PAUSER_ROLE) {
+        _pause();
+    }
+
+    function unpause() external onlyRole(PAUSER_ROLE) {
+        _unpause();
+    }
+
+    function setBlacklist(address account, bool status) external onlyRole(COMPLIANCE_ROLE) {
+        _blacklist[account] = status;
+        emit BlacklistUpdated(account, status);
+    }
+
+    function isBlacklisted(address account) external view returns (bool) {
+        return _blacklist[account];
+    }
+
+    function documentHashOf(uint256 tokenId) external view returns (bytes32) {
+        return _documentHash[tokenId];
+    }
+
+    function appraisalOf(uint256 tokenId) external view returns (uint256) {
+        return _appraisalValue[tokenId];
+    }
+
+    function safeMint(address to, string memory uri, bytes32 documentHash, uint256 appraisalValue) external onlyRole(MINTER_ROLE) returns (uint256) {
+        require(!_blacklist[to], "Blacklisted");
+        uint256 tokenId = ++_tokenIdCounter;
+        _safeMint(to, tokenId);
+        _setTokenURI(tokenId, uri);
+        _documentHash[tokenId] = documentHash;
+        _appraisalValue[tokenId] = appraisalValue;
+        emit DocumentMinted(tokenId, to, uri, documentHash, appraisalValue);
+        return tokenId;
+    }
+
+    function setTokenURI(uint256 tokenId, string memory uri) external onlyRole(METADATA_ROLE) {
+        require(_ownerOf(tokenId) != address(0), "Invalid token");
+        _setTokenURI(tokenId, uri);
+        emit TokenURIUpdated(tokenId, uri);
+    }
+
+    function setAppraisal(uint256 tokenId, uint256 newValue) external onlyRole(APPRAISER_ROLE) {
+        require(_ownerOf(tokenId) != address(0), "Invalid token");
+        uint256 old = _appraisalValue[tokenId];
+        _appraisalValue[tokenId] = newValue;
+        emit AppraisalUpdated(tokenId, old, newValue);
+    }
+
+    function approve(address to, uint256 tokenId) public override(ERC721, IERC721) {
+        require(!_blacklist[_msgSender()], "Blacklisted");
+        if (to != address(0)) {
+            require(!_blacklist[to], "Blacklisted");
+        }
+        super.approve(to, tokenId);
+    }
+
+    function setApprovalForAll(address operator, bool approved) public override(ERC721, IERC721) {
+        require(!_blacklist[_msgSender()], "Blacklisted");
+        if (approved) {
+            require(!_blacklist[operator], "Blacklisted");
+        }
+        super.setApprovalForAll(operator, approved);
+    }
+
+    function _update(address to, uint256 tokenId, address auth) internal override(ERC721, ERC721Pausable) returns (address) {
+        address from = _ownerOf(tokenId);
+        if (from != address(0)) {
+            require(!_blacklist[from], "Blacklisted");
+        }
+        if (to != address(0)) {
+            require(!_blacklist[to], "Blacklisted");
+        }
+        require(!_blacklist[auth], "Blacklisted");
+        address previousOwner = super._update(to, tokenId, auth);
+        if (to == address(0)) {
+            delete _documentHash[tokenId];
+            delete _appraisalValue[tokenId];
+        }
+        return previousOwner;
+    }
+
+    function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage, AccessControl) returns (bool) {
+        return super.supportsInterface(interfaceId);
+    }
+
+    function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) {
+        return super.tokenURI(tokenId);
+    }
+
+    function grantComplianceRole(address account) external onlyRole(DEFAULT_ADMIN_ROLE) {
+        _grantRole(COMPLIANCE_ROLE, account);
+    }
+}

+ 481 - 0
doc/EASYCLI.md

@@ -0,0 +1,481 @@
+# easycli - Documentação Completa
+
+Este doc cobre tudo do nosso CLI (easycli) pra interagir com:
+- EasyBRL (ERC20) – comando `coin`
+- EasyToken (ERC721) – comando `token`
+- Utilidades – comando `polygon`
+
+Dica: tudo que for valor on-chain usa UNIDADES BASE (inteiro). Para o eBRL, 6 casas decimais. Ex.: 1,23 eBRL = 1_230_000.
+
+## Pré-requisitos
+- Go 1.21+
+- Node RPC: `RPC_URL` ou `POLYGON_RPC_URL` (ou `AMOY_RPC_URL`)
+- Endereços dos contratos: `EASY_COIN_ADDR`, `EASY_TOKEN_ADDR`
+- Chave do admin: `PRIVATE_KEY` (ou `EASY_ADMIN_PRIVATE_KEY`/`EASY_ADMIM_PRIVATE_KEY`)
+- Opcional: chaves do usuário de teste (não-admin): `NOT_EASY_ADMIM_PUBLIC_KEY`, `NOT_EASY_ADMIN_PRIVATE_KEY`
+
+Arquivo .env pode ficar na raiz (../.env relativo a sdk/) ou em sdk/.env. Flags sempre sobrescrevem .env.
+
+Evite CRLF no .env (linha do Windows). Se aparecer erro tipo `invalid hex character '\r'`, troque pra LF.
+
+## Build e ajuda
+
+Na pasta sdk/
+
+```bash
+go mod tidy
+go build -o easycli .
+./easycli --help
+```
+
+Saída global (resumo):
+- Flags globais: `--rpc`, `--pk`, `--coin-addr`, `--token-addr`
+- Comandos: `coin`, `token`, `polygon`
+
+## Flags globais
+- `--rpc`: URL do RPC (sobrepõe .env)
+- `--pk`: chave privada do remetente (hex) para transações
+- `--coin-addr`: endereço do contrato ERC20 EasyBRL
+- `--token-addr`: endereço do contrato ERC721 EasyToken
+
+Exemplo geral:
+```bash
+./easycli --rpc $RPC_URL --coin-addr $EASY_COIN_ADDR coin info
+```
+
+---
+
+# Comando: polygon
+
+## polygon create-new-address
+Gera uma carteira nova.
+
+Exemplo:
+```bash
+./easycli polygon create-new-address
+```
+Saída esperada:
+```
+privateKey=0x<hex>
+publicKey=0x<hex>
+address=0x<addr>
+```
+
+---
+
+# Comando: coin (ERC20 EasyBRL)
+
+## coin info
+Mostra nome, símbolo, decimais, totalSupply, e paused.
+```bash
+./easycli --rpc $RPC_URL --coin-addr $EASY_COIN_ADDR coin info
+```
+Saída:
+```
+name=EasyBRL symbol=eBRL decimals=6 totalSupply=123456789 paused=false
+```
+
+## coin balance --address <addr>
+Consulta saldo em unidades base.
+```bash
+./easycli coin balance --address 0xAbc...
+```
+Saída:
+```
+1000000
+```
+
+## coin transfer --to <addr> --amount <base>
+Transfere tokens.
+```bash
+./easycli coin transfer --to 0xAbc... --amount 5000
+```
+Saída:
+```
+0x<txhash>
+```
+
+## coin approve --spender <addr> --amount <base>
+Aprova o spender pra gastar seus tokens.
+```bash
+./easycli coin approve --spender 0xSpender... --amount 20000
+```
+Saída:
+```
+0x<txhash>
+```
+
+## coin allowance --owner <addr> --spender <addr>
+Consulta allowance.
+```bash
+./easycli coin allowance --owner 0xOwner... --spender 0xSpender...
+```
+Saída:
+```
+20000
+```
+
+## coin transfer-from --from <addr> --to <addr> --amount <base>
+Transfere usando a allowance disponível.
+```bash
+./easycli coin transfer-from --from 0xUser... --to 0xAdmin... --amount 5000
+```
+Saída:
+```
+0x<txhash>
+```
+
+## coin paused
+Consulta se está pausado.
+```bash
+./easycli coin paused
+```
+Saída:
+```
+false
+```
+
+## coin pause
+Pausa transferências (precisa de papel apropriado – admin/pauser).
+```bash
+./easycli coin pause
+```
+Saída:
+```
+0x<txhash>
+```
+
+## coin unpause
+Despausa.
+```bash
+./easycli coin unpause
+```
+Saída:
+```
+0x<txhash>
+```
+
+## coin set-blacklist --account <addr> --status <true|false>
+Configura blacklist.
+```bash
+./easycli coin set-blacklist --account 0xUser... --status true
+```
+Saída:
+```
+0x<txhash>
+```
+
+## coin is-blacklisted --account <addr>
+Verifica blacklist.
+```bash
+./easycli coin is-blacklisted --account 0xUser...
+```
+Saída:
+```
+true
+```
+
+## coin mint --to <addr> --amount <base>
+Mint (admin).
+```bash
+./easycli coin mint --to 0xUser... --amount 100000
+```
+Saída:
+```
+0x<txhash>
+```
+
+## coin burn --amount <base>
+Burn do próprio saldo (admin).
+```bash
+./easycli coin burn --amount 10000
+```
+Saída:
+```
+0x<txhash>
+```
+
+## coin burn-from --account <addr> --amount <base>
+Burn de outro (conforme regras/allowance/role).
+```bash
+./easycli coin burn-from --account 0xUser... --amount 1
+```
+Saída:
+```
+0x<txhash>
+```
+
+## Roles (coin)
+Papéis: `DEFAULT_ADMIN_ROLE`, `PAUSER_ROLE`, `MINTER_ROLE`, `COMPLIANCE_ROLE`.
+
+### coin grant-role --role <ROLE> --account <addr>
+```bash
+./easycli coin grant-role --role COMPLIANCE_ROLE --account 0xUser...
+```
+Saída:
+```
+0x<txhash>
+```
+
+### coin revoke-role --role <ROLE> --account <addr>
+```bash
+./easycli coin revoke-role --role COMPLIANCE_ROLE --account 0xUser...
+```
+Saída:
+```
+0x<txhash>
+```
+
+### coin has-role --role <ROLE> --account <addr>
+```bash
+./easycli coin has-role --role COMPLIANCE_ROLE --account 0xUser...
+```
+Saída:
+```
+true
+```
+
+### coin grant-compliance-role --account <addr>
+Atalho para conceder COMPLIANCE_ROLE.
+```bash
+./easycli coin grant-compliance-role --account 0xUser...
+```
+Saída:
+```
+0x<txhash>
+```
+
+---
+
+# Comando: token (ERC721 EasyToken)
+
+## token info
+```bash
+./easycli --rpc $RPC_URL --token-addr $EASY_TOKEN_ADDR token info
+```
+Saída:
+```
+name=EasyToken symbol=eToken paused=false
+```
+
+## token owner-of --token-id <id>
+```bash
+./easycli token owner-of --token-id 1
+```
+Saída:
+```
+0xOwner...
+```
+
+## token balance --address <addr>
+Quantidade de NFTs do endereço (valor inteiro).
+```bash
+./easycli token balance --address 0xUser...
+```
+Saída:
+```
+2
+```
+
+## token token-uri --token-id <id>
+```bash
+./easycli token token-uri --token-id 1
+```
+Saída:
+```
+https://example.com/doc1.json
+```
+
+## token doc-hash --token-id <id>
+```bash
+./easycli token doc-hash --token-id 1
+```
+Saída:
+```
+0x<64-hex>
+```
+
+## token appraisal --token-id <id>
+Valor de avaliação (base units).
+```bash
+./easycli token appraisal --token-id 1
+```
+Saída:
+```
+1000000
+```
+
+## token set-token-uri --token-id <id> --uri <url>
+Requer `METADATA_ROLE`.
+```bash
+./easycli token set-token-uri --token-id 1 --uri https://example.com/doc1-updated.json
+```
+Saída:
+```
+0x<txhash>
+```
+
+## token set-appraisal --token-id <id> --value <base>
+Requer `APPRAISER_ROLE`.
+```bash
+./easycli token set-appraisal --token-id 1 --value 1200000
+```
+Saída:
+```
+0x<txhash>
+```
+
+## token safe-mint --to <addr> --uri <url> --hash <0x64hex> --value <base>
+Minte um novo NFT de documento.
+```bash
+./easycli token safe-mint --to 0xUser... --uri https://example.com/doc1.json --hash 0x<64-hex> --value 1000000
+```
+Saída:
+```
+0x<txhash>
+```
+
+## token transfer --to <addr> --token-id <id>
+Transfere seu NFT.
+```bash
+./easycli token transfer --to 0xTo... --token-id 1
+```
+Saída:
+```
+0x<txhash>
+```
+
+## token safe-transfer --to <addr> --token-id <id>
+Versão explícita de safeTransferFrom.
+```bash
+./easycli token safe-transfer --to 0xTo... --token-id 1
+```
+Saída:
+```
+0x<txhash>
+```
+
+## token approve --to <addr> --token-id <id>
+Aprova alguém pra esse tokenId.
+```bash
+./easycli token approve --to 0xSpender... --token-id 1
+```
+Saída:
+```
+0x<txhash>
+```
+
+## token set-approval-for-all --operator <addr> --approved <bool>
+Aprovação por operador.
+```bash
+./easycli token set-approval-for-all --operator 0xOp... --approved true
+```
+Saída:
+```
+0x<txhash>
+```
+
+## token get-approved --token-id <id>
+```bash
+./easycli token get-approved --token-id 1
+```
+Saída:
+```
+0xApproved...
+```
+
+## token is-approved-for-all --owner <addr> --operator <addr>
+```bash
+./easycli token is-approved-for-all --owner 0xOwner... --operator 0xOp...
+```
+Saída:
+```
+true
+```
+
+## token paused | token pause | token unpause
+```bash
+./easycli token paused
+./easycli token pause
+./easycli token unpause
+```
+Saídas:
+```
+false
+0x<txhash>
+0x<txhash>
+```
+
+## token set-blacklist --account <addr> --status <bool>
+```bash
+./easycli token set-blacklist --account 0xUser... --status true
+```
+Saída:
+```
+0x<txhash>
+```
+
+## token is-blacklisted --account <addr>
+```bash
+./easycli token is-blacklisted --account 0xUser...
+```
+Saída:
+```
+true
+```
+
+## Roles (token)
+Papéis: `DEFAULT_ADMIN_ROLE`, `PAUSER_ROLE`, `MINTER_ROLE`, `METADATA_ROLE`, `APPRAISER_ROLE`, `COMPLIANCE_ROLE`.
+
+### token grant-role --role <ROLE> --account <addr>
+```bash
+./easycli token grant-role --role MINTER_ROLE --account 0xAdmin...
+```
+Saída:
+```
+0x<txhash>
+```
+
+### token revoke-role --role <ROLE> --account <addr>
+```bash
+./easycli token revoke-role --role MINTER_ROLE --account 0xUser...
+```
+Saída:
+```
+0x<txhash>
+```
+
+### token has-role --role <ROLE> --account <addr>
+```bash
+./easycli token has-role --role MINTER_ROLE --account 0xUser...
+```
+Saída:
+```
+false
+```
+
+### token grant-compliance-role --account <addr>
+```bash
+./easycli token grant-compliance-role --account 0xUser...
+```
+Saída:
+```
+0x<txhash>
+```
+
+---
+
+# Geração de hash de documento (bytes32)
+Use um SHA-256 (64 hex) e prefixe com 0x. Exemplo rápido no Linux/macOS:
+```bash
+echo -n "meu-doc" | sha256sum | awk '{print $1}'
+# 0x<coloque-prefixo-0x-no-resultado>
+```
+
+# Dicas e solução de problemas
+- Erro `invalid hex character '\r'`: seu .env está com CRLF. Troque pra LF.
+- `missing address`/`invalid address`: confira EASY_COIN_ADDR/EASY_TOKEN_ADDR e se têm prefixo 0x.
+- `execution reverted`: geralmente falta de role, blacklist ou paused. Verifique permissões e status.
+
+# Testes E2E
+Temos um script de teste: `sdk/test_cli.sh` que roda cenários com admin e não-admin, barra de progresso e checklist final.

File diff suppressed because it is too large
+ 1168 - 0
doc/TETC - EasyToken White Paper.md


+ 54 - 0
hardhat.config.ts

@@ -0,0 +1,54 @@
+import type { HardhatUserConfig } from "hardhat/config";
+
+import hardhatToolboxMochaEthersPlugin from "@nomicfoundation/hardhat-toolbox-mocha-ethers";
+import { configVariable } from "hardhat/config";
+
+const config: HardhatUserConfig = {
+  plugins: [hardhatToolboxMochaEthersPlugin],
+  solidity: {
+    profiles: {
+      default: {
+        version: "0.8.28",
+      },
+      production: {
+        version: "0.8.28",
+        settings: {
+          optimizer: {
+            enabled: true,
+            runs: 200,
+          },
+        },
+      },
+    },
+  },
+  networks: {
+    hardhatMainnet: {
+      type: "edr-simulated",
+      chainType: "l1",
+    },
+    hardhatOp: {
+      type: "edr-simulated",
+      chainType: "op",
+    },
+    sepolia: {
+      type: "http",
+      chainType: "l1",
+      url: configVariable("SEPOLIA_RPC_URL"),
+      accounts: [configVariable("SEPOLIA_PRIVATE_KEY")],
+    },
+    polygon: {
+      type: "http",
+      chainType: "l1",
+      url: configVariable("POLYGON_RPC_URL"),
+      accounts: [configVariable("POLYGON_PRIVATE_KEY")],
+    },
+    amoy: {
+      type: "http",
+      chainType: "l1",
+      url: configVariable("AMOY_RPC_URL"),
+      accounts: [configVariable("AMOY_PRIVATE_KEY")],
+    },
+  },
+};
+
+export default config;

File diff suppressed because it is too large
+ 780 - 0
ignition/deployments/chain-137/artifacts/EasyTokensModule#EasyBRLStable.json


File diff suppressed because it is too large
+ 1110 - 0
ignition/deployments/chain-137/artifacts/EasyTokensModule#EasyTokenDocument.json


File diff suppressed because it is too large
+ 39 - 0
ignition/deployments/chain-137/build-info/solc-0_8_28-ddc46233633439e449b5a328d3d988c360ef7382.json


File diff suppressed because it is too large
+ 40 - 0
ignition/deployments/chain-137/build-info/solc-0_8_28-f9fa217b6eb02192b8f34c6bcdba9eedb4043d7e.json


+ 4 - 0
ignition/deployments/chain-137/deployed_addresses.json

@@ -0,0 +1,4 @@
+{
+  "EasyTokensModule#EasyBRLStable": "0x0D674e9384220bf9146fccD70E422B0480CC0a12",
+  "EasyTokensModule#EasyTokenDocument": "0xA325731B277424f4157B734729C733c46591bfE6"
+}

File diff suppressed because it is too large
+ 3 - 0
ignition/deployments/chain-137/journal.jsonl


+ 21 - 0
ignition/modules/EasyTokens.ts

@@ -0,0 +1,21 @@
+import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
+
+const EasyTokensModule = buildModule("EasyTokensModule", (m) => {
+  // Deployer/admin account (receives all roles in both constructors)
+  const admin = m.getAccount(0);
+
+  // Parameters with sensible defaults per project preferences
+  const erc20Name = m.getParameter("erc20Name", "EasyBRL");
+  const erc20Symbol = m.getParameter("erc20Symbol", "eBRL");
+
+  const erc721Name = m.getParameter("erc721Name", "EasyToken");
+  const erc721Symbol = m.getParameter("erc721Symbol", "eToken");
+
+  // Deploy ERC20 (EasyBRLStable) and ERC721 (EasyTokenDocument)
+  const easyBRLStable = m.contract("EasyBRLStable", [admin, erc20Name, erc20Symbol]);
+  const easyTokenDocument = m.contract("EasyTokenDocument", [admin, erc721Name, erc721Symbol]);
+
+  return { easyBRLStable, easyTokenDocument };
+});
+
+export default EasyTokensModule;

+ 3966 - 0
package-lock.json

@@ -0,0 +1,3966 @@
+{
+  "name": "sc",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "dependencies": {
+        "@nomicfoundation/hardhat-toolbox-mocha-ethers": "^3.0.0",
+        "@openzeppelin/contracts": "^5.4.0",
+        "hardhat": "^3.0.8"
+      }
+    },
+    "node_modules/@adraffy/ens-normalize": {
+      "version": "1.10.1",
+      "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz",
+      "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz",
+      "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==",
+      "cpu": [
+        "ppc64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz",
+      "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz",
+      "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz",
+      "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz",
+      "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz",
+      "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz",
+      "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz",
+      "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz",
+      "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz",
+      "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz",
+      "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==",
+      "cpu": [
+        "ia32"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz",
+      "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==",
+      "cpu": [
+        "loong64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz",
+      "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==",
+      "cpu": [
+        "mips64el"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz",
+      "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==",
+      "cpu": [
+        "ppc64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz",
+      "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==",
+      "cpu": [
+        "riscv64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz",
+      "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==",
+      "cpu": [
+        "s390x"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz",
+      "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-arm64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz",
+      "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz",
+      "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-arm64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz",
+      "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz",
+      "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openharmony-arm64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz",
+      "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openharmony"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz",
+      "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz",
+      "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz",
+      "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==",
+      "cpu": [
+        "ia32"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz",
+      "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@ethersproject/abi": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.8.0.tgz",
+      "integrity": "sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/address": "^5.8.0",
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/constants": "^5.8.0",
+        "@ethersproject/hash": "^5.8.0",
+        "@ethersproject/keccak256": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "@ethersproject/strings": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/abstract-provider": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.8.0.tgz",
+      "integrity": "sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/networks": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "@ethersproject/transactions": "^5.8.0",
+        "@ethersproject/web": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/abstract-signer": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.8.0.tgz",
+      "integrity": "sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/abstract-provider": "^5.8.0",
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/address": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.8.0.tgz",
+      "integrity": "sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/keccak256": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/rlp": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/base64": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.8.0.tgz",
+      "integrity": "sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/bytes": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/bignumber": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.8.0.tgz",
+      "integrity": "sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "bn.js": "^5.2.1"
+      }
+    },
+    "node_modules/@ethersproject/bytes": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.8.0.tgz",
+      "integrity": "sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/logger": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/constants": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.8.0.tgz",
+      "integrity": "sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/bignumber": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/hash": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.8.0.tgz",
+      "integrity": "sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/abstract-signer": "^5.8.0",
+        "@ethersproject/address": "^5.8.0",
+        "@ethersproject/base64": "^5.8.0",
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/keccak256": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "@ethersproject/strings": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/keccak256": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.8.0.tgz",
+      "integrity": "sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/bytes": "^5.8.0",
+        "js-sha3": "0.8.0"
+      }
+    },
+    "node_modules/@ethersproject/logger": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.8.0.tgz",
+      "integrity": "sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/@ethersproject/networks": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.8.0.tgz",
+      "integrity": "sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/logger": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/properties": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.8.0.tgz",
+      "integrity": "sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/logger": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/rlp": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.8.0.tgz",
+      "integrity": "sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/signing-key": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.8.0.tgz",
+      "integrity": "sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "bn.js": "^5.2.1",
+        "elliptic": "6.6.1",
+        "hash.js": "1.1.7"
+      }
+    },
+    "node_modules/@ethersproject/strings": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.8.0.tgz",
+      "integrity": "sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/constants": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/transactions": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.8.0.tgz",
+      "integrity": "sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/address": "^5.8.0",
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/constants": "^5.8.0",
+        "@ethersproject/keccak256": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "@ethersproject/rlp": "^5.8.0",
+        "@ethersproject/signing-key": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/web": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.8.0.tgz",
+      "integrity": "sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/base64": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "@ethersproject/strings": "^5.8.0"
+      }
+    },
+    "node_modules/@isaacs/cliui": {
+      "version": "8.0.2",
+      "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+      "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "string-width": "^5.1.2",
+        "string-width-cjs": "npm:string-width@^4.2.0",
+        "strip-ansi": "^7.0.1",
+        "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+        "wrap-ansi": "^8.1.0",
+        "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+      "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ansi-regex": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
+    "node_modules/@noble/ciphers": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.2.1.tgz",
+      "integrity": "sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": "^14.21.3 || >=16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@noble/curves": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz",
+      "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "1.4.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@noble/hashes": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz",
+      "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@nomicfoundation/edr": {
+      "version": "0.12.0-next.7",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.12.0-next.7.tgz",
+      "integrity": "sha512-w8blgst1EHXuvjYNSEkTYRFNiqYWi7T2+OurJasQI8W+uOYKeIFY5m9GPTwbMgeS+5Q/8QW1vLyxiR8RDHEvqA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 20"
+      },
+      "optionalDependencies": {
+        "@nomicfoundation/edr-darwin-arm64": "0.12.0-next.7",
+        "@nomicfoundation/edr-darwin-x64": "0.12.0-next.7",
+        "@nomicfoundation/edr-linux-arm64-gnu": "0.12.0-next.7",
+        "@nomicfoundation/edr-linux-arm64-musl": "0.12.0-next.7",
+        "@nomicfoundation/edr-linux-x64-gnu": "0.12.0-next.7",
+        "@nomicfoundation/edr-linux-x64-musl": "0.12.0-next.7",
+        "@nomicfoundation/edr-win32-x64-msvc": "0.12.0-next.7"
+      }
+    },
+    "node_modules/@nomicfoundation/edr-darwin-arm64": {
+      "version": "0.12.0-next.7",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.7.tgz",
+      "integrity": "sha512-i3TVHIqZJAXqUh3nR9rlAx/fUrWQDXRtJhEtM9ZwgmKQMYO5+QmcmcA58qo/Yw3xb44hOk1TX9JgSkpjCIhgaQ==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/@nomicfoundation/edr-darwin-x64": {
+      "version": "0.12.0-next.7",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.7.tgz",
+      "integrity": "sha512-vvoBA5nowhd56iPbDtDsIq+ZBHHWP4pwUC34zi0Eq7SSofll7MVm6KKYcEPrUsrjPtCWqswv6dnuuVJYS8U+kg==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/@nomicfoundation/edr-linux-arm64-gnu": {
+      "version": "0.12.0-next.7",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.7.tgz",
+      "integrity": "sha512-pXzAYhcVR+3udnylfNk4q/YUOMthhozUB/KxMV6V2oXX3X/SujB03m8JnoeUKsqVIVILbN9PVYz3V6rCjZg84g==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/@nomicfoundation/edr-linux-arm64-musl": {
+      "version": "0.12.0-next.7",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.7.tgz",
+      "integrity": "sha512-Ny463r0bZ97hfvCBpPZieDpPCuq9RXk3vrCTjCa8uHg+Tu8Prx3MvutbP1lJi5klO5qPVot+aikBgVZ+D0dG2g==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/@nomicfoundation/edr-linux-x64-gnu": {
+      "version": "0.12.0-next.7",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.7.tgz",
+      "integrity": "sha512-ST676U0TDc1Jh6U237a/NgcoxOh828T5+zrVNz7IENdMw6liZ4F/ubBZc+3MVtOqPFzPpIItz02DDjr+KhEISw==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/@nomicfoundation/edr-linux-x64-musl": {
+      "version": "0.12.0-next.7",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.7.tgz",
+      "integrity": "sha512-wCPFuf6bHH34fM0lClHuUoH57hJW/Rwh1TB0MqlZaiMhxPxdwVws28bAm4Rxr3OO7qNFn89pvG4yOhBcEqPdDg==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/@nomicfoundation/edr-win32-x64-msvc": {
+      "version": "0.12.0-next.7",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.7.tgz",
+      "integrity": "sha512-nhyPbpnKoZcKRsAciE4DNF0IDltBh7cuAgWGdtJt/TTwQcWTYdEeb6iYRlXGI9eUsFdhrnvwIRN7bKfkOBDbCQ==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/@nomicfoundation/hardhat-errors": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-errors/-/hardhat-errors-3.0.3.tgz",
+      "integrity": "sha512-qvVIyNE5yXFdwCD7G74fb3j+p5PjYSej/K2mhOuJBhxdGwzARpyoJbcDZrjkNyabytlt95iniZLHHWM9jvVXEA==",
+      "license": "MIT",
+      "dependencies": {
+        "@nomicfoundation/hardhat-utils": "^3.0.1"
+      }
+    },
+    "node_modules/@nomicfoundation/hardhat-ethers": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-4.0.2.tgz",
+      "integrity": "sha512-teW1GpImmpKF5pZGM5SVDHD10bGj5MafjdmmG/Ort0gRuPt0PZz1Up2ggJQPHdzP6/Q30Wgdmhg/qLGL/48lpw==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@nomicfoundation/hardhat-errors": "^3.0.2",
+        "@nomicfoundation/hardhat-utils": "^3.0.3",
+        "debug": "^4.3.2",
+        "ethereum-cryptography": "^2.2.1",
+        "ethers": "^6.14.0"
+      },
+      "peerDependencies": {
+        "hardhat": "^3.0.7"
+      }
+    },
+    "node_modules/@nomicfoundation/hardhat-ethers-chai-matchers": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers-chai-matchers/-/hardhat-ethers-chai-matchers-3.0.0.tgz",
+      "integrity": "sha512-IhDOUXv5+7cH1b8X5TX2qr2jLsMPjboFH/jXh7j090PbYWCRGwzdG3Olzzmd1X1WWpkw16nnz91+6pXuLpbQkw==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@nomicfoundation/hardhat-errors": "^3.0.0",
+        "@nomicfoundation/hardhat-utils": "^3.0.0",
+        "@types/chai-as-promised": "^8.0.1",
+        "chai-as-promised": "^8.0.0",
+        "deep-eql": "^5.0.1"
+      },
+      "peerDependencies": {
+        "@nomicfoundation/hardhat-ethers": "^4.0.0",
+        "chai": "^5.1.2",
+        "ethers": "^6.14.0",
+        "hardhat": "^3.0.0"
+      }
+    },
+    "node_modules/@nomicfoundation/hardhat-ignition": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ignition/-/hardhat-ignition-3.0.3.tgz",
+      "integrity": "sha512-wFHgY+JAaIFPBxyJbrNJl7TjDmXtFod3nPDTPTMerPP+i69yk4yl3CM8nvjUzTKSACf7Ci3xfNfoqc3voeuaLQ==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@nomicfoundation/hardhat-errors": "^3.0.2",
+        "@nomicfoundation/hardhat-utils": "^3.0.1",
+        "@nomicfoundation/ignition-core": "^3.0.3",
+        "@nomicfoundation/ignition-ui": "^3.0.3",
+        "chalk": "^5.3.0",
+        "debug": "^4.3.2",
+        "json5": "^2.2.3",
+        "prompts": "^2.4.2"
+      },
+      "peerDependencies": {
+        "@nomicfoundation/hardhat-verify": "^3.0.0",
+        "hardhat": "^3.0.0"
+      }
+    },
+    "node_modules/@nomicfoundation/hardhat-ignition-ethers": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ignition-ethers/-/hardhat-ignition-ethers-3.0.3.tgz",
+      "integrity": "sha512-TfiHLbOKGqDLmtIHpo3dKg9sPTx5HCPih2NdvQvRTI3kb2+GwBuFOYSF9k5jHXtSch25E9HfCc2aqA7SgsnS2A==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@nomicfoundation/hardhat-errors": "^3.0.2"
+      },
+      "peerDependencies": {
+        "@nomicfoundation/hardhat-ethers": "^4.0.0",
+        "@nomicfoundation/hardhat-ignition": "^3.0.2",
+        "@nomicfoundation/hardhat-verify": "^3.0.0",
+        "@nomicfoundation/ignition-core": "^3.0.2",
+        "ethers": "^6.14.0",
+        "hardhat": "^3.0.0"
+      }
+    },
+    "node_modules/@nomicfoundation/hardhat-keystore": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-keystore/-/hardhat-keystore-3.0.1.tgz",
+      "integrity": "sha512-IHjTWf88Kp6ZsnwngVYNJphWwwhnkSjg+wBd9im5yo8IbvCjd0Otyv2ucw0Dol+mxM3t/6XJhEnhbA3JGe/EdQ==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@noble/ciphers": "1.2.1",
+        "@noble/hashes": "1.7.1",
+        "@nomicfoundation/hardhat-errors": "^3.0.0",
+        "@nomicfoundation/hardhat-utils": "^3.0.0",
+        "@nomicfoundation/hardhat-zod-utils": "^3.0.0",
+        "chalk": "^5.3.0",
+        "debug": "^4.3.2",
+        "zod": "^3.23.8"
+      },
+      "peerDependencies": {
+        "hardhat": "^3.0.0"
+      }
+    },
+    "node_modules/@nomicfoundation/hardhat-keystore/node_modules/@noble/hashes": {
+      "version": "1.7.1",
+      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz",
+      "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": "^14.21.3 || >=16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@nomicfoundation/hardhat-mocha": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-mocha/-/hardhat-mocha-3.0.3.tgz",
+      "integrity": "sha512-84FKWng9dSP3JdsBRFy+vL3fEuskd+U6Qi7ccjEnuofvBiMm1JMpgJI48rgtG6kqSfFOL4NacPbs74oJk9+/GQ==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@nomicfoundation/hardhat-errors": "^3.0.3",
+        "@nomicfoundation/hardhat-utils": "^3.0.1",
+        "@nomicfoundation/hardhat-zod-utils": "^3.0.0",
+        "chalk": "^5.3.0",
+        "hardhat": "^3.0.8",
+        "mocha": "^11.0.0",
+        "tsx": "^4.19.3",
+        "zod": "^3.23.8"
+      }
+    },
+    "node_modules/@nomicfoundation/hardhat-network-helpers": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-3.0.1.tgz",
+      "integrity": "sha512-bNDZJawEEZhUQkUwvwlM5lLwcEBxQ5wNYDZsNHeiycPcYsDRhSFDuVPuKppJ78NGGnDUiwVsEogr/kRcQHk9rg==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@nomicfoundation/hardhat-errors": "^3.0.0",
+        "@nomicfoundation/hardhat-utils": "^3.0.3"
+      },
+      "peerDependencies": {
+        "hardhat": "^3.0.0"
+      }
+    },
+    "node_modules/@nomicfoundation/hardhat-toolbox-mocha-ethers": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-toolbox-mocha-ethers/-/hardhat-toolbox-mocha-ethers-3.0.0.tgz",
+      "integrity": "sha512-2WiLWh701im222k/rdSPaQSQzR5RdrpaIVbsGt9z42zqZoEmehoYidUcVrIWG1ocAvfHS9LuxHo6nBd0xMHRnQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@nomicfoundation/hardhat-ethers": "^4.0.0",
+        "@nomicfoundation/hardhat-ethers-chai-matchers": "^3.0.0",
+        "@nomicfoundation/hardhat-ignition": "^3.0.0",
+        "@nomicfoundation/hardhat-ignition-ethers": "^3.0.0",
+        "@nomicfoundation/hardhat-keystore": "^3.0.0",
+        "@nomicfoundation/hardhat-mocha": "^3.0.0",
+        "@nomicfoundation/hardhat-network-helpers": "^3.0.0",
+        "@nomicfoundation/hardhat-typechain": "^3.0.0",
+        "@nomicfoundation/hardhat-verify": "^3.0.0",
+        "@nomicfoundation/ignition-core": "^3.0.0",
+        "chai": "^5.1.2",
+        "ethers": "^6.14.0",
+        "hardhat": "^3.0.0"
+      }
+    },
+    "node_modules/@nomicfoundation/hardhat-typechain": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-typechain/-/hardhat-typechain-3.0.0.tgz",
+      "integrity": "sha512-wwPNrb+a/IrvMpj94VsXyKM64T0SJFXsmmqCHnGfh/iAdZmX5J9ILBLeDUE9JW5rL6ove8TB7OcXZkGp8unB4w==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@nomicfoundation/hardhat-errors": "^3.0.0",
+        "@nomicfoundation/hardhat-utils": "^3.0.0",
+        "@nomicfoundation/hardhat-zod-utils": "^3.0.0",
+        "@typechain/ethers-v6": "^0.5.0",
+        "debug": "^4.3.2",
+        "typechain": "^8.3.1",
+        "zod": "^3.23.8"
+      },
+      "peerDependencies": {
+        "@nomicfoundation/hardhat-ethers": "^4.0.0",
+        "ethers": "^6.14.0",
+        "hardhat": "^3.0.0"
+      }
+    },
+    "node_modules/@nomicfoundation/hardhat-utils": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-utils/-/hardhat-utils-3.0.3.tgz",
+      "integrity": "sha512-XNoDJshvGR3l8/oQdNoLPLlcv+P+mGZBLixCyuO8Q0wTIyH/1+KYI77E+yvgTyw+GG4UmldFw9R3Hmnemk3KCA==",
+      "license": "MIT",
+      "dependencies": {
+        "@streamparser/json-node": "^0.0.22",
+        "debug": "^4.3.2",
+        "env-paths": "^2.2.0",
+        "ethereum-cryptography": "^2.2.1",
+        "fast-equals": "^5.0.1",
+        "json-stream-stringify": "^3.1.6",
+        "rfdc": "^1.3.1",
+        "undici": "^6.16.1"
+      }
+    },
+    "node_modules/@nomicfoundation/hardhat-verify": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-verify/-/hardhat-verify-3.0.4.tgz",
+      "integrity": "sha512-2EeteIxYL+teGDDm/NxMXmyiclAeg3XLAgGRL3CWCjEpxHX0xHa1sQOIsBkTs+DaVZnPCOY/s6/F+xvXgfoVyQ==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/abi": "^5.8.0",
+        "@nomicfoundation/hardhat-errors": "^3.0.3",
+        "@nomicfoundation/hardhat-utils": "^3.0.3",
+        "@nomicfoundation/hardhat-zod-utils": "^3.0.0",
+        "cbor2": "^1.9.0",
+        "chalk": "^5.3.0",
+        "debug": "^4.3.2",
+        "semver": "^7.6.3",
+        "zod": "^3.23.8"
+      },
+      "peerDependencies": {
+        "hardhat": "^3.0.0"
+      }
+    },
+    "node_modules/@nomicfoundation/hardhat-zod-utils": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-zod-utils/-/hardhat-zod-utils-3.0.1.tgz",
+      "integrity": "sha512-I6/pyYiS9p2lLkzQuedr1ScMocH+ew8l233xTi+LP92gjEiviJDxselpkzgU01MUM0t6BPpfP8yMO958LDEJVg==",
+      "license": "MIT",
+      "dependencies": {
+        "@nomicfoundation/hardhat-errors": "^3.0.0",
+        "@nomicfoundation/hardhat-utils": "^3.0.2"
+      },
+      "peerDependencies": {
+        "zod": "^3.23.8"
+      }
+    },
+    "node_modules/@nomicfoundation/ignition-core": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/ignition-core/-/ignition-core-3.0.3.tgz",
+      "integrity": "sha512-XlExOpCNyjLhAnfo/RaAknQ/0nYXl5+0+3Cg7T3zZAh/nEC52gecuhjG27jhIdtts2Oo78bwCsvnY2Wp0kHhpg==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/address": "5.6.1",
+        "@nomicfoundation/hardhat-errors": "^3.0.2",
+        "@nomicfoundation/hardhat-utils": "^3.0.1",
+        "@nomicfoundation/solidity-analyzer": "^0.1.1",
+        "cbor2": "^1.9.0",
+        "debug": "^4.3.2",
+        "ethers": "^6.14.0",
+        "immer": "10.0.2",
+        "lodash-es": "4.17.21",
+        "ndjson": "2.0.0"
+      }
+    },
+    "node_modules/@nomicfoundation/ignition-core/node_modules/@ethersproject/address": {
+      "version": "5.6.1",
+      "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.6.1.tgz",
+      "integrity": "sha512-uOgF0kS5MJv9ZvCz7x6T2EXJSzotiybApn4XlOgoTX0xdtyVIJ7pF+6cGPxiEq/dpBiTfMiw7Yc81JcwhSYA0Q==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ethersproject/bignumber": "^5.6.2",
+        "@ethersproject/bytes": "^5.6.1",
+        "@ethersproject/keccak256": "^5.6.1",
+        "@ethersproject/logger": "^5.6.0",
+        "@ethersproject/rlp": "^5.6.1"
+      }
+    },
+    "node_modules/@nomicfoundation/ignition-ui": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/ignition-ui/-/ignition-ui-3.0.3.tgz",
+      "integrity": "sha512-9xpwKi68P4ckMucpNnoKlDei2A3wgbPAnJN8ouYxGZpPtzT0kTfDtRCPAQEGqPoQiOWAn84jojVYrJmL+RQS+A==",
+      "peer": true
+    },
+    "node_modules/@nomicfoundation/solidity-analyzer": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.2.tgz",
+      "integrity": "sha512-q4n32/FNKIhQ3zQGGw5CvPF6GTvDCpYwIf7bEY/dZTZbgfDsHyjJwURxUJf3VQuuJj+fDIFl4+KkBVbw4Ef6jA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12"
+      },
+      "optionalDependencies": {
+        "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.1.2",
+        "@nomicfoundation/solidity-analyzer-darwin-x64": "0.1.2",
+        "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.1.2",
+        "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.1.2",
+        "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.1.2",
+        "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.2",
+        "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.1.2"
+      }
+    },
+    "node_modules/@nomicfoundation/solidity-analyzer-darwin-arm64": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.2.tgz",
+      "integrity": "sha512-JaqcWPDZENCvm++lFFGjrDd8mxtf+CtLd2MiXvMNTBD33dContTZ9TWETwNFwg7JTJT5Q9HEecH7FA+HTSsIUw==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/@nomicfoundation/solidity-analyzer-darwin-x64": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.2.tgz",
+      "integrity": "sha512-fZNmVztrSXC03e9RONBT+CiksSeYcxI1wlzqyr0L7hsQlK1fzV+f04g2JtQ1c/Fe74ZwdV6aQBdd6Uwl1052sw==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-gnu": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.2.tgz",
+      "integrity": "sha512-3d54oc+9ZVBuB6nbp8wHylk4xh0N0Gc+bk+/uJae+rUgbOBwQSfuGIbAZt1wBXs5REkSmynEGcqx6DutoK0tPA==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-musl": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.2.tgz",
+      "integrity": "sha512-iDJfR2qf55vgsg7BtJa7iPiFAsYf2d0Tv/0B+vhtnI16+wfQeTbP7teookbGvAo0eJo7aLLm0xfS/GTkvHIucA==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-gnu": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.2.tgz",
+      "integrity": "sha512-9dlHMAt5/2cpWyuJ9fQNOUXFB/vgSFORg1jpjX1Mh9hJ/MfZXlDdHQ+DpFCs32Zk5pxRBb07yGvSHk9/fezL+g==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-musl": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.2.tgz",
+      "integrity": "sha512-GzzVeeJob3lfrSlDKQw2bRJ8rBf6mEYaWY+gW0JnTDHINA0s2gPR4km5RLIj1xeZZOYz4zRw+AEeYgLRqB2NXg==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/@nomicfoundation/solidity-analyzer-win32-x64-msvc": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.2.tgz",
+      "integrity": "sha512-Fdjli4DCcFHb4Zgsz0uEJXZ2K7VEO+w5KVv7HmT7WO10iODdU9csC2az4jrhEsRtiR9Gfd74FlG0NYlw1BMdyA==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/@openzeppelin/contracts": {
+      "version": "5.4.0",
+      "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.4.0.tgz",
+      "integrity": "sha512-eCYgWnLg6WO+X52I16TZt8uEjbtdkgLC0SUX/xnAksjjrQI4Xfn4iBRoI5j55dmlOhDv1Y7BoR3cU7e3WWhC6A==",
+      "license": "MIT"
+    },
+    "node_modules/@pkgjs/parseargs": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+      "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+      "license": "MIT",
+      "optional": true,
+      "peer": true,
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@scure/base": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz",
+      "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@scure/bip32": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz",
+      "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/curves": "~1.4.0",
+        "@noble/hashes": "~1.4.0",
+        "@scure/base": "~1.1.6"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@scure/bip39": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz",
+      "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "~1.4.0",
+        "@scure/base": "~1.1.6"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@sentry/core": {
+      "version": "9.46.0",
+      "resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.46.0.tgz",
+      "integrity": "sha512-it7JMFqxVproAgEtbLgCVBYtQ9fIb+Bu0JD+cEplTN/Ukpe6GaolyYib5geZqslVxhp2sQgT+58aGvfd/k0N8Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@streamparser/json": {
+      "version": "0.0.22",
+      "resolved": "https://registry.npmjs.org/@streamparser/json/-/json-0.0.22.tgz",
+      "integrity": "sha512-b6gTSBjJ8G8SuO3Gbbj+zXbVx8NSs1EbpbMKpzGLWMdkR+98McH9bEjSz3+0mPJf68c5nxa3CrJHp5EQNXM6zQ==",
+      "license": "MIT"
+    },
+    "node_modules/@streamparser/json-node": {
+      "version": "0.0.22",
+      "resolved": "https://registry.npmjs.org/@streamparser/json-node/-/json-node-0.0.22.tgz",
+      "integrity": "sha512-sJT2ptNRwqB1lIsQrQlCoWk5rF4tif9wDh+7yluAGijJamAhrHGYpFB/Zg3hJeceoZypi74ftXk8DHzwYpbZSg==",
+      "license": "MIT",
+      "dependencies": {
+        "@streamparser/json": "^0.0.22"
+      }
+    },
+    "node_modules/@typechain/ethers-v6": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/@typechain/ethers-v6/-/ethers-v6-0.5.1.tgz",
+      "integrity": "sha512-F+GklO8jBWlsaVV+9oHaPh5NJdd6rAKN4tklGfInX1Q7h0xPgVLP39Jl3eCulPB5qexI71ZFHwbljx4ZXNfouA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "lodash": "^4.17.15",
+        "ts-essentials": "^7.0.1"
+      },
+      "peerDependencies": {
+        "ethers": "6.x",
+        "typechain": "^8.3.2",
+        "typescript": ">=4.7.0"
+      }
+    },
+    "node_modules/@types/chai": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
+      "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/deep-eql": "*",
+        "assertion-error": "^2.0.1"
+      }
+    },
+    "node_modules/@types/chai-as-promised": {
+      "version": "8.0.2",
+      "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-8.0.2.tgz",
+      "integrity": "sha512-meQ1wDr1K5KRCSvG2lX7n7/5wf70BeptTKst0axGvnN6zqaVpRqegoIbugiAPSqOW9K9aL8gDVrm7a2LXOtn2Q==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/chai": "*"
+      }
+    },
+    "node_modules/@types/deep-eql": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
+      "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/@types/node": {
+      "version": "22.7.5",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz",
+      "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "undici-types": "~6.19.2"
+      }
+    },
+    "node_modules/@types/prettier": {
+      "version": "2.7.3",
+      "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz",
+      "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/adm-zip": {
+      "version": "0.4.16",
+      "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
+      "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.3.0"
+      }
+    },
+    "node_modules/aes-js": {
+      "version": "4.0.0-beta.5",
+      "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz",
+      "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/ansi-colors": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
+      "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+      "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "license": "Python-2.0",
+      "peer": true
+    },
+    "node_modules/array-back": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz",
+      "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/assertion-error": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+      "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/bn.js": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz",
+      "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/brace-expansion": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+      "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/brorand": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+      "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/browser-stdout": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+      "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+      "license": "ISC",
+      "peer": true
+    },
+    "node_modules/camelcase": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+      "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cbor2": {
+      "version": "1.12.0",
+      "resolved": "https://registry.npmjs.org/cbor2/-/cbor2-1.12.0.tgz",
+      "integrity": "sha512-3Cco8XQhi27DogSp9Ri6LYNZLi/TBY/JVnDe+mj06NkBjW/ZYOtekaEU4wZ4xcRMNrFkDv8KNtOAqHyDfz3lYg==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=18.7"
+      }
+    },
+    "node_modules/chai": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz",
+      "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "assertion-error": "^2.0.1",
+        "check-error": "^2.1.1",
+        "deep-eql": "^5.0.1",
+        "loupe": "^3.1.0",
+        "pathval": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/chai-as-promised": {
+      "version": "8.0.2",
+      "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-8.0.2.tgz",
+      "integrity": "sha512-1GadL+sEJVLzDjcawPM4kjfnL+p/9vrxiEUonowKOAzvVg0PixJUdtuDzdkDeQhK3zfOE76GqGkZIQ7/Adcrqw==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "check-error": "^2.1.1"
+      },
+      "peerDependencies": {
+        "chai": ">= 2.1.2 < 7"
+      }
+    },
+    "node_modules/chalk": {
+      "version": "5.6.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+      "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.17.0 || ^14.13 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/check-error": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
+      "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 16"
+      }
+    },
+    "node_modules/chokidar": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+      "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+      "license": "MIT",
+      "dependencies": {
+        "readdirp": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 14.16.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/cliui": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/cliui/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/cliui/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/cliui/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cliui/node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/command-line-args": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz",
+      "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "array-back": "^3.1.0",
+        "find-replace": "^3.0.0",
+        "lodash.camelcase": "^4.3.0",
+        "typical": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/command-line-usage": {
+      "version": "6.1.3",
+      "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz",
+      "integrity": "sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "array-back": "^4.0.2",
+        "chalk": "^2.4.2",
+        "table-layout": "^1.0.2",
+        "typical": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/command-line-usage/node_modules/ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "color-convert": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/command-line-usage/node_modules/array-back": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz",
+      "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/command-line-usage/node_modules/chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/command-line-usage/node_modules/color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "color-name": "1.1.3"
+      }
+    },
+    "node_modules/command-line-usage/node_modules/color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/command-line-usage/node_modules/escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/command-line-usage/node_modules/has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/command-line-usage/node_modules/supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/command-line-usage/node_modules/typical": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz",
+      "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/cross-spawn": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/debug": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/decamelize": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+      "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/deep-eql": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
+      "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/deep-extend": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+      "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/diff": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz",
+      "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==",
+      "license": "BSD-3-Clause",
+      "peer": true,
+      "engines": {
+        "node": ">=0.3.1"
+      }
+    },
+    "node_modules/eastasianwidth": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+      "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/elliptic": {
+      "version": "6.6.1",
+      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz",
+      "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "bn.js": "^4.11.9",
+        "brorand": "^1.1.0",
+        "hash.js": "^1.0.0",
+        "hmac-drbg": "^1.0.1",
+        "inherits": "^2.0.4",
+        "minimalistic-assert": "^1.0.1",
+        "minimalistic-crypto-utils": "^1.0.1"
+      }
+    },
+    "node_modules/elliptic/node_modules/bn.js": {
+      "version": "4.12.2",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz",
+      "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/emoji-regex": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/enquirer": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
+      "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-colors": "^4.1.1",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/env-paths": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+      "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.25.11",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz",
+      "integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.25.11",
+        "@esbuild/android-arm": "0.25.11",
+        "@esbuild/android-arm64": "0.25.11",
+        "@esbuild/android-x64": "0.25.11",
+        "@esbuild/darwin-arm64": "0.25.11",
+        "@esbuild/darwin-x64": "0.25.11",
+        "@esbuild/freebsd-arm64": "0.25.11",
+        "@esbuild/freebsd-x64": "0.25.11",
+        "@esbuild/linux-arm": "0.25.11",
+        "@esbuild/linux-arm64": "0.25.11",
+        "@esbuild/linux-ia32": "0.25.11",
+        "@esbuild/linux-loong64": "0.25.11",
+        "@esbuild/linux-mips64el": "0.25.11",
+        "@esbuild/linux-ppc64": "0.25.11",
+        "@esbuild/linux-riscv64": "0.25.11",
+        "@esbuild/linux-s390x": "0.25.11",
+        "@esbuild/linux-x64": "0.25.11",
+        "@esbuild/netbsd-arm64": "0.25.11",
+        "@esbuild/netbsd-x64": "0.25.11",
+        "@esbuild/openbsd-arm64": "0.25.11",
+        "@esbuild/openbsd-x64": "0.25.11",
+        "@esbuild/openharmony-arm64": "0.25.11",
+        "@esbuild/sunos-x64": "0.25.11",
+        "@esbuild/win32-arm64": "0.25.11",
+        "@esbuild/win32-ia32": "0.25.11",
+        "@esbuild/win32-x64": "0.25.11"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ethereum-cryptography": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz",
+      "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/curves": "1.4.2",
+        "@noble/hashes": "1.4.0",
+        "@scure/bip32": "1.4.0",
+        "@scure/bip39": "1.3.0"
+      }
+    },
+    "node_modules/ethers": {
+      "version": "6.15.0",
+      "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.15.0.tgz",
+      "integrity": "sha512-Kf/3ZW54L4UT0pZtsY/rf+EkBU7Qi5nnhonjUb8yTXcxH3cdcWrV2cRyk0Xk/4jK6OoHhxxZHriyhje20If2hQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/ethers-io/"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@adraffy/ens-normalize": "1.10.1",
+        "@noble/curves": "1.2.0",
+        "@noble/hashes": "1.3.2",
+        "@types/node": "22.7.5",
+        "aes-js": "4.0.0-beta.5",
+        "tslib": "2.7.0",
+        "ws": "8.17.1"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/ethers/node_modules/@noble/curves": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
+      "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@noble/hashes": "1.3.2"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/ethers/node_modules/@noble/hashes": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
+      "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/ethers/node_modules/ws": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
+      "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/fast-equals": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.3.2.tgz",
+      "integrity": "sha512-6rxyATwPCkaFIL3JLqw8qXqMpIZ942pTX/tbQFkRsDGblS8tNGtlUauA/+mt6RUfqn/4MoEr+WDkYoIQbibWuQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/find-replace": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz",
+      "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "array-back": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/flat": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+      "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+      "license": "BSD-3-Clause",
+      "peer": true,
+      "bin": {
+        "flat": "cli.js"
+      }
+    },
+    "node_modules/foreground-child": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+      "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "cross-spawn": "^7.0.6",
+        "signal-exit": "^4.0.1"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/fs-extra": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
+      "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "graceful-fs": "^4.1.2",
+        "jsonfile": "^4.0.0",
+        "universalify": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=6 <7 || >=8"
+      }
+    },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+      "license": "ISC",
+      "peer": true
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "license": "ISC",
+      "peer": true,
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
+    },
+    "node_modules/get-tsconfig": {
+      "version": "4.12.0",
+      "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.12.0.tgz",
+      "integrity": "sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==",
+      "license": "MIT",
+      "dependencies": {
+        "resolve-pkg-maps": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+      }
+    },
+    "node_modules/glob": {
+      "version": "10.4.5",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+      "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "foreground-child": "^3.1.0",
+        "jackspeak": "^3.1.2",
+        "minimatch": "^9.0.4",
+        "minipass": "^7.1.2",
+        "package-json-from-dist": "^1.0.0",
+        "path-scurry": "^1.11.1"
+      },
+      "bin": {
+        "glob": "dist/esm/bin.mjs"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "license": "ISC",
+      "peer": true
+    },
+    "node_modules/hardhat": {
+      "version": "3.0.8",
+      "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.0.8.tgz",
+      "integrity": "sha512-OebUJWdqnYtcDtmuhMc8bnuTrMOMxVyPkl1kUX0YgwiMui37fzSv2qjmbVgE9LCuIyuGxBKkor6CQmvOheSygA==",
+      "license": "MIT",
+      "dependencies": {
+        "@nomicfoundation/edr": "0.12.0-next.7",
+        "@nomicfoundation/hardhat-errors": "^3.0.3",
+        "@nomicfoundation/hardhat-utils": "^3.0.3",
+        "@nomicfoundation/hardhat-zod-utils": "^3.0.1",
+        "@nomicfoundation/solidity-analyzer": "^0.1.1",
+        "@sentry/core": "^9.4.0",
+        "adm-zip": "^0.4.16",
+        "chalk": "^5.3.0",
+        "chokidar": "^4.0.3",
+        "debug": "^4.3.2",
+        "enquirer": "^2.3.0",
+        "ethereum-cryptography": "^2.2.1",
+        "micro-eth-signer": "^0.14.0",
+        "p-map": "^7.0.2",
+        "resolve.exports": "^2.0.3",
+        "semver": "^7.6.3",
+        "tsx": "^4.19.3",
+        "ws": "^8.18.0",
+        "zod": "^3.23.8"
+      },
+      "bin": {
+        "hardhat": "dist/src/cli.js"
+      }
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/hash.js": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+      "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "minimalistic-assert": "^1.0.1"
+      }
+    },
+    "node_modules/he": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+      "license": "MIT",
+      "peer": true,
+      "bin": {
+        "he": "bin/he"
+      }
+    },
+    "node_modules/hmac-drbg": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+      "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "hash.js": "^1.0.3",
+        "minimalistic-assert": "^1.0.0",
+        "minimalistic-crypto-utils": "^1.0.1"
+      }
+    },
+    "node_modules/immer": {
+      "version": "10.0.2",
+      "resolved": "https://registry.npmjs.org/immer/-/immer-10.0.2.tgz",
+      "integrity": "sha512-Rx3CqeqQ19sxUtYV9CU911Vhy8/721wRFnJv3REVGWUmoAcIwzifTsdmJte/MV+0/XpM35LZdQMBGkRIoLPwQA==",
+      "license": "MIT",
+      "peer": true,
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/immer"
+      }
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "license": "ISC",
+      "peer": true
+    },
+    "node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-path-inside": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+      "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-plain-obj": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+      "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-unicode-supported": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+      "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+      "license": "ISC",
+      "peer": true
+    },
+    "node_modules/jackspeak": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+      "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+      "license": "BlueOak-1.0.0",
+      "peer": true,
+      "dependencies": {
+        "@isaacs/cliui": "^8.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      },
+      "optionalDependencies": {
+        "@pkgjs/parseargs": "^0.11.0"
+      }
+    },
+    "node_modules/js-sha3": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
+      "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/json-stream-stringify": {
+      "version": "3.1.6",
+      "resolved": "https://registry.npmjs.org/json-stream-stringify/-/json-stream-stringify-3.1.6.tgz",
+      "integrity": "sha512-x7fpwxOkbhFCaJDJ8vb1fBY3DdSa4AlITaz+HHILQJzdPMnHEFjxPwVUi1ALIbcIxDE0PNe/0i7frnY8QnBQog==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=7.10.1"
+      }
+    },
+    "node_modules/json-stringify-safe": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+      "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+      "license": "ISC",
+      "peer": true
+    },
+    "node_modules/json5": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "license": "MIT",
+      "peer": true,
+      "bin": {
+        "json5": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/jsonfile": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+      "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+      "license": "MIT",
+      "peer": true,
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/kleur": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+      "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "p-locate": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/lodash-es": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+      "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/lodash.camelcase": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+      "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/log-symbols": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "is-unicode-supported": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/log-symbols/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/log-symbols/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/log-symbols/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/loupe": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
+      "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/lru-cache": {
+      "version": "10.4.3",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "license": "ISC",
+      "peer": true
+    },
+    "node_modules/micro-eth-signer": {
+      "version": "0.14.0",
+      "resolved": "https://registry.npmjs.org/micro-eth-signer/-/micro-eth-signer-0.14.0.tgz",
+      "integrity": "sha512-5PLLzHiVYPWClEvZIXXFu5yutzpadb73rnQCpUqIHu3No3coFuWQNfE5tkBQJ7djuLYl6aRLaS0MgWJYGoqiBw==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/curves": "~1.8.1",
+        "@noble/hashes": "~1.7.1",
+        "micro-packed": "~0.7.2"
+      }
+    },
+    "node_modules/micro-eth-signer/node_modules/@noble/curves": {
+      "version": "1.8.2",
+      "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.2.tgz",
+      "integrity": "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "1.7.2"
+      },
+      "engines": {
+        "node": "^14.21.3 || >=16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/micro-eth-signer/node_modules/@noble/hashes": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.2.tgz",
+      "integrity": "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ==",
+      "license": "MIT",
+      "engines": {
+        "node": "^14.21.3 || >=16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/micro-packed": {
+      "version": "0.7.3",
+      "resolved": "https://registry.npmjs.org/micro-packed/-/micro-packed-0.7.3.tgz",
+      "integrity": "sha512-2Milxs+WNC00TRlem41oRswvw31146GiSaoCT7s3Xi2gMUglW5QBeqlQaZeHr5tJx9nm3i57LNXPqxOOaWtTYg==",
+      "license": "MIT",
+      "dependencies": {
+        "@scure/base": "~1.2.5"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/micro-packed/node_modules/@scure/base": {
+      "version": "1.2.6",
+      "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz",
+      "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/minimalistic-assert": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+      "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+      "license": "ISC",
+      "peer": true
+    },
+    "node_modules/minimalistic-crypto-utils": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+      "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/minimatch": {
+      "version": "9.0.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "license": "MIT",
+      "peer": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/minipass": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+      "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+      "license": "ISC",
+      "peer": true,
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      }
+    },
+    "node_modules/mkdirp": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+      "license": "MIT",
+      "peer": true,
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/mocha": {
+      "version": "11.7.4",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.4.tgz",
+      "integrity": "sha512-1jYAaY8x0kAZ0XszLWu14pzsf4KV740Gld4HXkhNTXwcHx4AUEDkPzgEHg9CM5dVcW+zv036tjpsEbLraPJj4w==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "browser-stdout": "^1.3.1",
+        "chokidar": "^4.0.1",
+        "debug": "^4.3.5",
+        "diff": "^7.0.0",
+        "escape-string-regexp": "^4.0.0",
+        "find-up": "^5.0.0",
+        "glob": "^10.4.5",
+        "he": "^1.2.0",
+        "is-path-inside": "^3.0.3",
+        "js-yaml": "^4.1.0",
+        "log-symbols": "^4.1.0",
+        "minimatch": "^9.0.5",
+        "ms": "^2.1.3",
+        "picocolors": "^1.1.1",
+        "serialize-javascript": "^6.0.2",
+        "strip-json-comments": "^3.1.1",
+        "supports-color": "^8.1.1",
+        "workerpool": "^9.2.0",
+        "yargs": "^17.7.2",
+        "yargs-parser": "^21.1.1",
+        "yargs-unparser": "^2.0.0"
+      },
+      "bin": {
+        "_mocha": "bin/_mocha",
+        "mocha": "bin/mocha.js"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/ndjson": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ndjson/-/ndjson-2.0.0.tgz",
+      "integrity": "sha512-nGl7LRGrzugTtaFcJMhLbpzJM6XdivmbkdlaGcrk/LXg2KL/YBC6z1g70xh0/al+oFuVFP8N8kiWRucmeEH/qQ==",
+      "license": "BSD-3-Clause",
+      "peer": true,
+      "dependencies": {
+        "json-stringify-safe": "^5.0.1",
+        "minimist": "^1.2.5",
+        "readable-stream": "^3.6.0",
+        "split2": "^3.0.0",
+        "through2": "^4.0.0"
+      },
+      "bin": {
+        "ndjson": "cli.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "yocto-queue": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "p-limit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-map": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz",
+      "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/package-json-from-dist": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+      "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+      "license": "BlueOak-1.0.0",
+      "peer": true
+    },
+    "node_modules/path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-scurry": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+      "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+      "license": "BlueOak-1.0.0",
+      "peer": true,
+      "dependencies": {
+        "lru-cache": "^10.2.0",
+        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/pathval": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
+      "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 14.16"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+      "license": "ISC",
+      "peer": true
+    },
+    "node_modules/prettier": {
+      "version": "2.8.8",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
+      "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
+      "license": "MIT",
+      "peer": true,
+      "bin": {
+        "prettier": "bin-prettier.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "url": "https://github.com/prettier/prettier?sponsor=1"
+      }
+    },
+    "node_modules/prompts": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
+      "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "kleur": "^3.0.3",
+        "sisteransi": "^1.0.5"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/readdirp": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
+      "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14.18.0"
+      },
+      "funding": {
+        "type": "individual",
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/reduce-flatten": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz",
+      "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/resolve-pkg-maps": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+      "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+      }
+    },
+    "node_modules/resolve.exports": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz",
+      "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/rfdc": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+      "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+      "license": "MIT"
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/semver": {
+      "version": "7.7.3",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+      "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/serialize-javascript": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+      "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
+      "license": "BSD-3-Clause",
+      "peer": true,
+      "dependencies": {
+        "randombytes": "^2.1.0"
+      }
+    },
+    "node_modules/shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "shebang-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/signal-exit": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+      "license": "ISC",
+      "peer": true,
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/sisteransi": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+      "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/split2": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
+      "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "readable-stream": "^3.0.0"
+      }
+    },
+    "node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "node_modules/string-format": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz",
+      "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==",
+      "license": "WTFPL OR MIT",
+      "peer": true
+    },
+    "node_modules/string-width": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+      "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "eastasianwidth": "^0.2.0",
+        "emoji-regex": "^9.2.2",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/string-width-cjs": {
+      "name": "string-width",
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/string-width-cjs/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/string-width/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/string-width/node_modules/strip-ansi": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+      "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ansi-regex": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-ansi-cjs": {
+      "name": "strip-ansi",
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/supports-color": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/supports-color?sponsor=1"
+      }
+    },
+    "node_modules/table-layout": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz",
+      "integrity": "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "array-back": "^4.0.1",
+        "deep-extend": "~0.6.0",
+        "typical": "^5.2.0",
+        "wordwrapjs": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/table-layout/node_modules/array-back": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz",
+      "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/table-layout/node_modules/typical": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz",
+      "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/through2": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
+      "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "readable-stream": "3"
+      }
+    },
+    "node_modules/ts-command-line-args": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz",
+      "integrity": "sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw==",
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "command-line-args": "^5.1.1",
+        "command-line-usage": "^6.1.0",
+        "string-format": "^2.0.0"
+      },
+      "bin": {
+        "write-markdown": "dist/write-markdown.js"
+      }
+    },
+    "node_modules/ts-command-line-args/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/ts-command-line-args/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/ts-command-line-args/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ts-essentials": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz",
+      "integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==",
+      "license": "MIT",
+      "peer": true,
+      "peerDependencies": {
+        "typescript": ">=3.7.0"
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
+      "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
+      "license": "0BSD",
+      "peer": true
+    },
+    "node_modules/tsx": {
+      "version": "4.20.6",
+      "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz",
+      "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==",
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "~0.25.0",
+        "get-tsconfig": "^4.7.5"
+      },
+      "bin": {
+        "tsx": "dist/cli.mjs"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      }
+    },
+    "node_modules/typechain": {
+      "version": "8.3.2",
+      "resolved": "https://registry.npmjs.org/typechain/-/typechain-8.3.2.tgz",
+      "integrity": "sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/prettier": "^2.1.1",
+        "debug": "^4.3.1",
+        "fs-extra": "^7.0.0",
+        "glob": "7.1.7",
+        "js-sha3": "^0.8.0",
+        "lodash": "^4.17.15",
+        "mkdirp": "^1.0.4",
+        "prettier": "^2.3.1",
+        "ts-command-line-args": "^2.2.0",
+        "ts-essentials": "^7.0.1"
+      },
+      "bin": {
+        "typechain": "dist/cli/cli.js"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.3.0"
+      }
+    },
+    "node_modules/typechain/node_modules/brace-expansion": {
+      "version": "1.1.12",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+      "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/typechain/node_modules/glob": {
+      "version": "7.1.7",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+      "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
+      "deprecated": "Glob versions prior to v9 are no longer supported",
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/typechain/node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "5.9.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+      "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+      "license": "Apache-2.0",
+      "peer": true,
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/typical": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz",
+      "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/undici": {
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/undici/-/undici-6.22.0.tgz",
+      "integrity": "sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18.17"
+      }
+    },
+    "node_modules/undici-types": {
+      "version": "6.19.8",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+      "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/universalify": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 4.0.0"
+      }
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/wordwrapjs": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz",
+      "integrity": "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "reduce-flatten": "^2.0.0",
+        "typical": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/wordwrapjs/node_modules/typical": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz",
+      "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/workerpool": {
+      "version": "9.3.4",
+      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz",
+      "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==",
+      "license": "Apache-2.0",
+      "peer": true
+    },
+    "node_modules/wrap-ansi": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+      "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ansi-styles": "^6.1.0",
+        "string-width": "^5.0.1",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/wrap-ansi-cjs": {
+      "name": "wrap-ansi",
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/strip-ansi": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+      "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ansi-regex": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "license": "ISC",
+      "peer": true
+    },
+    "node_modules/ws": {
+      "version": "8.18.3",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
+      "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "license": "ISC",
+      "peer": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yargs": {
+      "version": "17.7.2",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+      "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yargs-parser": {
+      "version": "21.1.1",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+      "license": "ISC",
+      "peer": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yargs-unparser": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
+      "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "camelcase": "^6.0.0",
+        "decamelize": "^4.0.0",
+        "flat": "^5.0.2",
+        "is-plain-obj": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yargs/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/yargs/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/zod": {
+      "version": "3.25.76",
+      "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
+      "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/colinhacks"
+      }
+    }
+  }
+}

+ 8 - 0
package.json

@@ -0,0 +1,8 @@
+{
+  "dependencies": {
+    "@nomicfoundation/hardhat-toolbox-mocha-ethers": "^3.0.0",
+    "@openzeppelin/contracts": "^5.4.0",
+    "hardhat": "^3.0.8"
+  },
+  "type": "module"
+}

+ 22 - 0
scripts/send-op-tx.ts

@@ -0,0 +1,22 @@
+import { network } from "hardhat";
+
+const { ethers } = await network.connect({
+  network: "hardhatOp",
+  chainType: "op",
+});
+
+console.log("Sending transaction using the OP chain type");
+
+const [sender] = await ethers.getSigners();
+
+console.log("Sending 1 wei from", sender.address, "to itself");
+
+console.log("Sending L2 transaction");
+const tx = await sender.sendTransaction({
+  to: sender.address,
+  value: 1n,
+});
+
+await tx.wait();
+
+console.log("Transaction sent successfully");

+ 53 - 0
sdk/abi.go

@@ -0,0 +1,53 @@
+package main
+
+const easyBRLStableABI = `[
+  {"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},
+  {"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},
+  {"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},
+  {"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},
+  {"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},
+  {"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},
+  {"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},
+  {"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},
+  {"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"status","type":"bool"}],"name":"setBlacklist","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isBlacklisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},
+  {"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"grantComplianceRole","outputs":[],"stateMutability":"nonpayable","type":"function"}
+]`
+
+const easyTokenDocumentABI = `[
+  {"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},
+  {"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},
+  {"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},
+  {"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},
+  {"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},
+  {"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},
+  {"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},
+  {"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},
+  {"inputs":[],"name":"valueDecimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},
+  {"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"documentHashOf","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},
+  {"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"appraisalOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},
+  {"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"uri","type":"string"}],"name":"setTokenURI","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"setAppraisal","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"status","type":"bool"}],"name":"setBlacklist","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isBlacklisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},
+  {"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},
+  {"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"string","name":"uri","type":"string"},{"internalType":"bytes32","name":"documentHash","type":"bytes32"},{"internalType":"uint256","name":"appraisalValue","type":"uint256"}],"name":"safeMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"grantComplianceRole","outputs":[],"stateMutability":"nonpayable","type":"function"}
+]`

BIN
sdk/easycli


+ 40 - 0
sdk/go.mod

@@ -0,0 +1,40 @@
+module sdk
+
+go 1.22
+
+require (
+	github.com/ethereum/go-ethereum v1.14.12
+	github.com/joho/godotenv v1.5.1
+	github.com/spf13/cobra v1.8.1
+)
+
+require (
+	github.com/Microsoft/go-winio v0.6.2 // indirect
+	github.com/StackExchange/wmi v1.2.1 // indirect
+	github.com/bits-and-blooms/bitset v1.13.0 // indirect
+	github.com/consensys/bavard v0.1.13 // indirect
+	github.com/consensys/gnark-crypto v0.12.1 // indirect
+	github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect
+	github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect
+	github.com/deckarep/golang-set/v2 v2.6.0 // indirect
+	github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
+	github.com/ethereum/c-kzg-4844 v1.0.0 // indirect
+	github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect
+	github.com/fsnotify/fsnotify v1.6.0 // indirect
+	github.com/go-ole/go-ole v1.3.0 // indirect
+	github.com/google/uuid v1.3.0 // indirect
+	github.com/gorilla/websocket v1.4.2 // indirect
+	github.com/holiman/uint256 v1.3.1 // indirect
+	github.com/inconshreveable/mousetrap v1.1.0 // indirect
+	github.com/mmcloughlin/addchain v0.4.0 // indirect
+	github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
+	github.com/spf13/pflag v1.0.5 // indirect
+	github.com/supranational/blst v0.3.13 // indirect
+	github.com/tklauser/go-sysconf v0.3.12 // indirect
+	github.com/tklauser/numcpus v0.6.1 // indirect
+	golang.org/x/crypto v0.22.0 // indirect
+	golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
+	golang.org/x/sync v0.7.0 // indirect
+	golang.org/x/sys v0.22.0 // indirect
+	rsc.io/tmplfunc v0.0.3 // indirect
+)

+ 187 - 0
sdk/go.sum

@@ -0,0 +1,187 @@
+github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
+github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
+github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
+github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
+github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
+github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
+github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI=
+github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
+github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
+github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
+github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I=
+github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8=
+github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4=
+github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
+github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
+github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
+github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA=
+github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU=
+github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
+github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
+github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
+github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
+github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
+github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
+github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M=
+github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
+github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
+github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I=
+github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs=
+github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI=
+github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
+github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
+github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
+github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
+github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA=
+github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
+github.com/ethereum/go-ethereum v1.14.12 h1:8hl57x77HSUo+cXExrURjU/w1VhL+ShCTJrTwcCQSe4=
+github.com/ethereum/go-ethereum v1.14.12/go.mod h1:RAC2gVMWJ6FkxSPESfbshrcKpIokgQKsVKmAuqdekDY=
+github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A=
+github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
+github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
+github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
+github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
+github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
+github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
+github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
+github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
+github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
+github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
+github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE=
+github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
+github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4=
+github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc=
+github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
+github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
+github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs=
+github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
+github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
+github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
+github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
+github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
+github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
+github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
+github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
+github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
+github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
+github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
+github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
+github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
+github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
+github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.12.0 h1:C+UIj/QWtmqY13Arb8kwMt5j34/0Z2iKamrJ+ryC0Gg=
+github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
+github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a h1:CmF68hwI0XsOQ5UwlBopMi2Ow4Pbg32akc4KIVCOm+Y=
+github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
+github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
+github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
+github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
+github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
+github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
+github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
+github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
+github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk=
+github.com/supranational/blst v0.3.13/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
+github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
+github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
+github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
+github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
+github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
+github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
+github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
+github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
+github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
+github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
+golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
+golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
+golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
+google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
+rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=

+ 923 - 0
sdk/main.go

@@ -0,0 +1,923 @@
+package main
+
+import (
+	"context"
+	"crypto/ecdsa"
+	"errors"
+	"fmt"
+	"log"
+	"math/big"
+	"os"
+	"strings"
+	"time"
+
+	"github.com/ethereum/go-ethereum/accounts/abi"
+	"github.com/ethereum/go-ethereum/accounts/abi/bind"
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/crypto"
+	"github.com/ethereum/go-ethereum/ethclient"
+	"github.com/joho/godotenv"
+	"github.com/spf13/cobra"
+)
+
+var (
+	rpcURL    string
+	coinAddr  string
+	tokenAddr string
+	pkHex     string
+)
+
+func loadEnv() {
+	_ = godotenv.Load("../.env")
+	_ = godotenv.Load(".env")
+
+	if rpcURL == "" {
+		rpcURL = os.Getenv("RPC_URL")
+		if rpcURL == "" {
+			rpcURL = os.Getenv("POLYGON_RPC_URL")
+			if rpcURL == "" {
+				rpcURL = os.Getenv("AMOY_RPC_URL")
+			}
+		}
+	}
+	if coinAddr == "" {
+		coinAddr = os.Getenv("EASY_COIN_ADDR")
+	}
+	if tokenAddr == "" {
+		tokenAddr = os.Getenv("EASY_TOKEN_ADDR")
+	}
+	if pkHex == "" {
+		pkHex = os.Getenv("PRIVATE_KEY")
+		if pkHex == "" {
+			pkHex = os.Getenv("EASY_ADMIN_PRIVATE_KEY")
+			if pkHex == "" {
+				pkHex = os.Getenv("EASY_ADMIM_PRIVATE_KEY") // fallback to example typo
+			}
+		}
+	}
+}
+
+func mustClient(ctx context.Context) (*ethclient.Client, *big.Int) {
+	if rpcURL == "" {
+		log.Fatal("RPC URL not set: use --rpc or set RPC_URL/POLYGON_RPC_URL/AMOY_RPC_URL in .env")
+	}
+	c, err := ethclient.DialContext(ctx, rpcURL)
+	if err != nil {
+		log.Fatalf("dial rpc: %v", err)
+	}
+	chainID, err := c.ChainID(ctx)
+	if err != nil {
+		log.Fatalf("get chain id: %v", err)
+	}
+	return c, chainID
+}
+
+func mustPrivKey() *ecdsa.PrivateKey {
+	if pkHex == "" {
+		log.Fatal("Private key not set: use --pk or set PRIVATE_KEY/EASY_ADMIN_PRIVATE_KEY in .env")
+	}
+	pk, err := crypto.HexToECDSA(strings.TrimPrefix(pkHex, "0x"))
+	if err != nil {
+		log.Fatalf("invalid private key: %v", err)
+	}
+	return pk
+}
+
+func fromHex32(s string) ([32]byte, error) {
+	var out [32]byte
+	b := common.FromHex(s)
+	if len(b) != 32 {
+		return out, fmt.Errorf("expected 32 bytes, got %d", len(b))
+	}
+	copy(out[:], b)
+	return out, nil
+}
+
+func roleID(name string) [32]byte {
+	upper := strings.ToUpper(name)
+	if upper == "DEFAULT_ADMIN_ROLE" || upper == "DEFAULT_ADMIN" || upper == "ADMIN" {
+		return [32]byte{}
+	}
+	// roles are keccak256("<ROLE>"), e.g. "PAUSER_ROLE"
+	h := crypto.Keccak256([]byte(upper))
+	var out [32]byte
+	copy(out[:], h)
+	return out
+}
+
+func mustAddress(hex string, envName string) common.Address {
+	if hex == "" {
+		log.Fatalf("missing address: set %s or pass a flag", envName)
+	}
+	if !common.IsHexAddress(hex) {
+		log.Fatalf("invalid address for %s: %s", envName, hex)
+	}
+	return common.HexToAddress(hex)
+}
+
+func toWei(amount string) (*big.Int, error) {
+	// parse base-unit integer (no decimals handling here)
+	if strings.Contains(amount, ".") {
+		return nil, errors.New("amount must be an integer in base units (no decimal point)")
+	}
+	z := new(big.Int)
+	_, ok := z.SetString(amount, 10)
+	if !ok {
+		return nil, fmt.Errorf("invalid amount: %s", amount)
+	}
+	return z, nil
+}
+
+func boundContract(addr common.Address, abiJSON string, c *ethclient.Client) *bind.BoundContract {
+	parsed, err := abi.JSON(strings.NewReader(abiJSON))
+	if err != nil {
+		log.Fatalf("parse abi: %v", err)
+	}
+	return bind.NewBoundContract(addr, parsed, c, c, c)
+}
+
+func txOpts(ctx context.Context, c *ethclient.Client, chainID *big.Int) *bind.TransactOpts {
+	pk := mustPrivKey()
+	auth, err := bind.NewKeyedTransactorWithChainID(pk, chainID)
+	if err != nil {
+		log.Fatalf("transactor: %v", err)
+	}
+	auth.Context = ctx
+	// let node estimate gas; we can set a reasonable timeout
+	return auth
+}
+
+func callOpts(ctx context.Context) *bind.CallOpts {
+	return &bind.CallOpts{Context: ctx}
+}
+
+// ABI call helpers compatible with go-ethereum v1.14 BoundContract.Call
+func callOut(ctx context.Context, con *bind.BoundContract, method string, args ...interface{}) ([]interface{}, error) {
+	var out []interface{}
+	if err := con.Call(callOpts(ctx), &out, method, args...); err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func callString(ctx context.Context, con *bind.BoundContract, method string, args ...interface{}) (string, error) {
+	out, err := callOut(ctx, con, method, args...)
+	if err != nil { return "", err }
+	v, ok := out[0].(string)
+	if !ok { return "", fmt.Errorf("unexpected %T", out[0]) }
+	return v, nil
+}
+
+func callBool(ctx context.Context, con *bind.BoundContract, method string, args ...interface{}) (bool, error) {
+	out, err := callOut(ctx, con, method, args...)
+	if err != nil { return false, err }
+	v, ok := out[0].(bool)
+	if !ok { return false, fmt.Errorf("unexpected %T", out[0]) }
+	return v, nil
+}
+
+func callUint8(ctx context.Context, con *bind.BoundContract, method string, args ...interface{}) (uint8, error) {
+	out, err := callOut(ctx, con, method, args...)
+	if err != nil { return 0, err }
+	v, ok := out[0].(uint8)
+	if !ok { return 0, fmt.Errorf("unexpected %T", out[0]) }
+	return v, nil
+}
+
+func callBig(ctx context.Context, con *bind.BoundContract, method string, args ...interface{}) (*big.Int, error) {
+	out, err := callOut(ctx, con, method, args...)
+	if err != nil { return nil, err }
+	v, ok := out[0].(*big.Int)
+	if !ok { return nil, fmt.Errorf("unexpected %T", out[0]) }
+	return v, nil
+}
+
+func callAddress(ctx context.Context, con *bind.BoundContract, method string, args ...interface{}) (common.Address, error) {
+	out, err := callOut(ctx, con, method, args...)
+	if err != nil { return common.Address{}, err }
+	v, ok := out[0].(common.Address)
+	if !ok { return common.Address{}, fmt.Errorf("unexpected %T", out[0]) }
+	return v, nil
+}
+
+func callBytes32(ctx context.Context, con *bind.BoundContract, method string, args ...interface{}) ([32]byte, error) {
+	out, err := callOut(ctx, con, method, args...)
+	if err != nil { return [32]byte{}, err }
+	v, ok := out[0].([32]byte)
+	if !ok { return [32]byte{}, fmt.Errorf("unexpected %T", out[0]) }
+	return v, nil
+}
+
+func cmdCoin() *cobra.Command {
+	coinCmd := &cobra.Command{
+		Use:   "coin",
+		Short: "Interact with EasyBRL (ERC20)",
+	}
+
+	// info
+	coinCmd.AddCommand(&cobra.Command{
+		Use:   "info",
+		Short: "Show ERC20 name, symbol, decimals, totalSupply, paused",
+		Run: func(cmd *cobra.Command, args []string) {
+			loadEnv()
+			ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+			defer cancel()
+			c, _ := mustClient(ctx)
+			addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+			con := boundContract(addr, easyBRLStableABI, c)
+			name, err := callString(ctx, con, "name"); if err != nil { log.Fatal(err) }
+			symbol, err := callString(ctx, con, "symbol"); if err != nil { log.Fatal(err) }
+			decimals, err := callUint8(ctx, con, "decimals"); if err != nil { log.Fatal(err) }
+			total, err := callBig(ctx, con, "totalSupply"); if err != nil { log.Fatal(err) }
+			paused, err := callBool(ctx, con, "paused"); if err != nil { log.Fatal(err) }
+			fmt.Printf("name=%s symbol=%s decimals=%d totalSupply=%s paused=%v\n", name, symbol, decimals, total.String(), paused)
+		},
+	})
+
+	// balance
+	var balAddr string
+	balCmd := &cobra.Command{
+		Use:   "balance",
+		Short: "Get ERC20 balance of an address",
+		Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if balAddr == "" { log.Fatal("--address required") }
+			ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel()
+			c, _ := mustClient(ctx)
+			addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+			con := boundContract(addr, easyBRLStableABI, c)
+			bal, err := callBig(ctx, con, "balanceOf", common.HexToAddress(balAddr)); if err != nil { log.Fatal(err) }
+			fmt.Println(bal.String())
+		},
+	}
+	balCmd.Flags().StringVar(&balAddr, "address", "", "Address to query")
+	coinCmd.AddCommand(balCmd)
+
+	// transfer
+	var trTo, trAmt string
+	trCmd := &cobra.Command{
+		Use:   "transfer",
+		Short: "Transfer ERC20 tokens (base units)",
+		Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if trTo == "" || trAmt == "" { log.Fatal("--to and --amount required") }
+			amt, err := toWei(trAmt); if err != nil { log.Fatal(err) }
+			ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second); defer cancel()
+			c, chain := mustClient(ctx)
+			auth := txOpts(ctx, c, chain)
+			addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+			con := boundContract(addr, easyBRLStableABI, c)
+			tx, err := con.Transact(auth, "transfer", common.HexToAddress(trTo), amt); if err != nil { log.Fatal(err) }
+			fmt.Println(tx.Hash().Hex())
+		},
+	}
+	trCmd.Flags().StringVar(&trTo, "to", "", "Recipient address")
+	trCmd.Flags().StringVar(&trAmt, "amount", "", "Amount in base units")
+	coinCmd.AddCommand(trCmd)
+
+	// approve
+	var apSp, apAmt string
+	apCmd := &cobra.Command{
+		Use:   "approve",
+		Short: "Approve spender (base units)",
+		Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if apSp == "" || apAmt == "" { log.Fatal("--spender and --amount required") }
+			amt, err := toWei(apAmt); if err != nil { log.Fatal(err) }
+			ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second); defer cancel()
+			c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+			addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+			con := boundContract(addr, easyBRLStableABI, c)
+			tx, err := con.Transact(auth, "approve", common.HexToAddress(apSp), amt); if err != nil { log.Fatal(err) }
+			fmt.Println(tx.Hash().Hex())
+		},
+	}
+	apCmd.Flags().StringVar(&apSp, "spender", "", "Spender address")
+	apCmd.Flags().StringVar(&apAmt, "amount", "", "Amount in base units")
+	coinCmd.AddCommand(apCmd)
+
+	// allowance
+	var alOwn, alSp string
+	alCmd := &cobra.Command{
+		Use:   "allowance",
+		Short: "Check allowance(owner,spender)",
+		Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if alOwn == "" || alSp == "" { log.Fatal("--owner and --spender required") }
+			ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel()
+			c, _ := mustClient(ctx)
+			addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+			con := boundContract(addr, easyBRLStableABI, c)
+			v, err := callBig(ctx, con, "allowance", common.HexToAddress(alOwn), common.HexToAddress(alSp)); if err != nil { log.Fatal(err) }
+			fmt.Println(v.String())
+		},
+	}
+	alCmd.Flags().StringVar(&alOwn, "owner", "", "Owner address")
+	alCmd.Flags().StringVar(&alSp, "spender", "", "Spender address")
+	coinCmd.AddCommand(alCmd)
+
+	// transfer-from
+	var tfFrom, tfTo, tfAmt string
+	tfCmd := &cobra.Command{
+		Use:   "transfer-from",
+		Short: "Transfer from (requires allowance)",
+		Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if tfFrom == "" || tfTo == "" || tfAmt == "" { log.Fatal("--from --to --amount required") }
+			amt, err := toWei(tfAmt); if err != nil { log.Fatal(err) }
+			ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second); defer cancel()
+			c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+			addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+			con := boundContract(addr, easyBRLStableABI, c)
+			tx, err := con.Transact(auth, "transferFrom", common.HexToAddress(tfFrom), common.HexToAddress(tfTo), amt); if err != nil { log.Fatal(err) }
+			fmt.Println(tx.Hash().Hex())
+		},
+	}
+	tfCmd.Flags().StringVar(&tfFrom, "from", "", "From address")
+	tfCmd.Flags().StringVar(&tfTo, "to", "", "To address")
+	tfCmd.Flags().StringVar(&tfAmt, "amount", "", "Amount in base units")
+	coinCmd.AddCommand(tfCmd)
+
+	// paused
+	coinCmd.AddCommand(&cobra.Command{Use: "paused", Short: "Is paused?", Run: func(cmd *cobra.Command, args []string) {
+		loadEnv(); ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel()
+		c, _ := mustClient(ctx)
+		addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+		con := boundContract(addr, easyBRLStableABI, c)
+		p, err := callBool(ctx, con, "paused"); if err != nil { log.Fatal(err) }
+		fmt.Println(p)
+	}})
+
+	// pause/unpause
+	coinCmd.AddCommand(&cobra.Command{Use: "pause", Short: "Pause transfers", Run: func(cmd *cobra.Command, args []string) {
+		loadEnv(); ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second); defer cancel()
+		c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+		addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+		con := boundContract(addr, easyBRLStableABI, c)
+		tx, err := con.Transact(auth, "pause"); if err != nil { log.Fatal(err) }
+		fmt.Println(tx.Hash().Hex())
+	}})
+	coinCmd.AddCommand(&cobra.Command{Use: "unpause", Short: "Unpause transfers", Run: func(cmd *cobra.Command, args []string) {
+		loadEnv(); ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second); defer cancel()
+		c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+		addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+		con := boundContract(addr, easyBRLStableABI, c)
+		tx, err := con.Transact(auth, "unpause"); if err != nil { log.Fatal(err) }
+		fmt.Println(tx.Hash().Hex())
+	}})
+
+	// blacklist
+	var blAcc string; var blStatus bool
+	blCmd := &cobra.Command{Use: "set-blacklist", Short: "Set blacklist status", Run: func(cmd *cobra.Command, args []string) {
+		loadEnv(); if blAcc=="" { log.Fatal("--account required") }
+		ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second); defer cancel()
+		c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+		addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+		con := boundContract(addr, easyBRLStableABI, c)
+		tx, err := con.Transact(auth, "setBlacklist", common.HexToAddress(blAcc), blStatus); if err != nil { log.Fatal(err) }
+		fmt.Println(tx.Hash().Hex())
+	}}
+	blCmd.Flags().StringVar(&blAcc, "account", "", "Account address")
+	blCmd.Flags().BoolVar(&blStatus, "status", false, "Blacklist status")
+	coinCmd.AddCommand(blCmd)
+
+	var qblAcc string
+	{
+		cmd := &cobra.Command{Use: "is-blacklisted", Short: "Check blacklist", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if qblAcc=="" { log.Fatal("--account required") }
+			ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel()
+			c, _ := mustClient(ctx)
+			addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+			con := boundContract(addr, easyBRLStableABI, c)
+			v, err := callBool(ctx, con, "isBlacklisted", common.HexToAddress(qblAcc)); if err != nil { log.Fatal(err) }
+			fmt.Println(v)
+		}}
+		cmd.Flags().StringVar(&qblAcc, "account", "", "Account address")
+		coinCmd.AddCommand(cmd)
+	}
+
+	// mint
+	var miTo, miAmt string
+	miCmd := &cobra.Command{Use: "mint", Short: "Mint tokens (admin only)", Run: func(cmd *cobra.Command, args []string) {
+		loadEnv(); if miTo==""||miAmt=="" { log.Fatal("--to --amount required") }
+		amt, err := toWei(miAmt); if err!=nil { log.Fatal(err) }
+		ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second); defer cancel()
+		c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+		addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+		con := boundContract(addr, easyBRLStableABI, c)
+		tx, err := con.Transact(auth, "mint", common.HexToAddress(miTo), amt); if err != nil { log.Fatal(err) }
+		fmt.Println(tx.Hash().Hex())
+	}}
+	miCmd.Flags().StringVar(&miTo, "to", "", "Recipient address")
+	miCmd.Flags().StringVar(&miAmt, "amount", "", "Amount in base units")
+	coinCmd.AddCommand(miCmd)
+
+	// burn & burn-from
+	var buAmt string
+	{
+		cmd := &cobra.Command{Use: "burn", Short: "Burn own balance (admin only)", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if buAmt=="" { log.Fatal("--amount required") }
+			amt, err := toWei(buAmt); if err!=nil { log.Fatal(err) }
+			ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second); defer cancel()
+			c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+			addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+			con := boundContract(addr, easyBRLStableABI, c)
+			tx, err := con.Transact(auth, "burn", amt); if err != nil { log.Fatal(err) }
+			fmt.Println(tx.Hash().Hex())
+		}}
+		cmd.Flags().StringVar(&buAmt, "amount", "", "Amount in base units")
+		coinCmd.AddCommand(cmd)
+	}
+
+	var bfAcc, bfAmt string
+	bfCmd := &cobra.Command{Use: "burn-from", Short: "Burn from account (admin only)", Run: func(cmd *cobra.Command, args []string) {
+		loadEnv(); if bfAcc==""||bfAmt=="" { log.Fatal("--account --amount required") }
+		amt, err := toWei(bfAmt); if err!=nil { log.Fatal(err) }
+		ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second); defer cancel()
+		c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+		addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+		con := boundContract(addr, easyBRLStableABI, c)
+		tx, err := con.Transact(auth, "burnFrom", common.HexToAddress(bfAcc), amt); if err != nil { log.Fatal(err) }
+		fmt.Println(tx.Hash().Hex())
+	}}
+	bfCmd.Flags().StringVar(&bfAcc, "account", "", "Account")
+	bfCmd.Flags().StringVar(&bfAmt, "amount", "", "Amount in base units")
+	coinCmd.AddCommand(bfCmd)
+
+	// roles
+	var rrRole, rrAcc string
+	grCmd := &cobra.Command{Use: "grant-role", Short: "Grant role to account", Run: func(cmd *cobra.Command, args []string) {
+		loadEnv(); if rrRole==""||rrAcc=="" { log.Fatal("--role --account required") }
+		ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second); defer cancel()
+		c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+		addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+		con := boundContract(addr, easyBRLStableABI, c)
+		role := roleID(rrRole)
+		tx, err := con.Transact(auth, "grantRole", role, common.HexToAddress(rrAcc)); if err != nil { log.Fatal(err) }
+		fmt.Println(tx.Hash().Hex())
+	}}
+	grCmd.Flags().StringVar(&rrRole, "role", "", "Role name (DEFAULT_ADMIN_ROLE, PAUSER_ROLE, MINTER_ROLE, COMPLIANCE_ROLE)")
+	grCmd.Flags().StringVar(&rrAcc, "account", "", "Account address")
+	coinCmd.AddCommand(grCmd)
+
+	rrRole, rrAcc = "", ""
+	rvCmd := &cobra.Command{Use: "revoke-role", Short: "Revoke role from account", Run: func(cmd *cobra.Command, args []string) {
+		loadEnv(); if rrRole==""||rrAcc=="" { log.Fatal("--role --account required") }
+		ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second); defer cancel()
+		c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+		addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+		con := boundContract(addr, easyBRLStableABI, c)
+		role := roleID(rrRole)
+		tx, err := con.Transact(auth, "revokeRole", role, common.HexToAddress(rrAcc)); if err != nil { log.Fatal(err) }
+		fmt.Println(tx.Hash().Hex())
+	}}
+	rvCmd.Flags().StringVar(&rrRole, "role", "", "Role name")
+	rvCmd.Flags().StringVar(&rrAcc, "account", "", "Account address")
+	coinCmd.AddCommand(rvCmd)
+
+	rrRole, rrAcc = "", ""
+	hasCmd := &cobra.Command{Use: "has-role", Short: "Check role for account", Run: func(cmd *cobra.Command, args []string) {
+		loadEnv(); if rrRole==""||rrAcc=="" { log.Fatal("--role --account required") }
+		ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel()
+		c, _ := mustClient(ctx)
+		addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+		con := boundContract(addr, easyBRLStableABI, c)
+		role := roleID(rrRole)
+		v, err := callBool(ctx, con, "hasRole", role, common.HexToAddress(rrAcc)); if err != nil { log.Fatal(err) }
+		fmt.Println(v)
+	}} 
+	hasCmd.Flags().StringVar(&rrRole, "role", "", "Role name")
+	hasCmd.Flags().StringVar(&rrAcc, "account", "", "Account address")
+	coinCmd.AddCommand(hasCmd)
+
+	var gcAcc string
+	{
+		cmd := &cobra.Command{Use: "grant-compliance-role", Short: "Grant COMPLIANCE_ROLE via helper", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if gcAcc=="" { log.Fatal("--account required") }
+			ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second); defer cancel()
+			c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+			addr := mustAddress(coinAddr, "EASY_COIN_ADDR")
+			con := boundContract(addr, easyBRLStableABI, c)
+			tx, err := con.Transact(auth, "grantComplianceRole", common.HexToAddress(gcAcc)); if err != nil { log.Fatal(err) }
+			fmt.Println(tx.Hash().Hex())
+		}}
+		cmd.Flags().StringVar(&gcAcc, "account", "", "Account address")
+		coinCmd.AddCommand(cmd)
+	}
+
+	return coinCmd
+}
+
+func cmdToken() *cobra.Command {
+    tokCmd := &cobra.Command{Use: "token", Short: "Interact with EasyToken (ERC721)"}
+
+	// info
+	tokCmd.AddCommand(&cobra.Command{Use: "info", Short: "Show ERC721 name, symbol, paused", Run: func(cmd *cobra.Command, args []string) {
+		loadEnv(); ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel()
+		c, _ := mustClient(ctx)
+		addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+		con := boundContract(addr, easyTokenDocumentABI, c)
+		name, err := callString(ctx, con, "name"); if err != nil { log.Fatal(err) }
+		symbol, err := callString(ctx, con, "symbol"); if err != nil { log.Fatal(err) }
+		paused, err := callBool(ctx, con, "paused"); if err != nil { log.Fatal(err) }
+		fmt.Printf("name=%s symbol=%s paused=%v\n", name, symbol, paused)
+	}})
+
+	// owner-of
+	var ooID string
+	{
+		cmd := &cobra.Command{Use: "owner-of", Short: "Owner of tokenId", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if ooID=="" { log.Fatal("--token-id required") }
+			id, ok := new(big.Int).SetString(ooID, 10); if !ok { log.Fatal("invalid token-id") }
+			ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel()
+			c, _ := mustClient(ctx)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			owner, err := callAddress(ctx, con, "ownerOf", id); if err != nil { log.Fatal(err) }
+			fmt.Println(owner.Hex())
+		}}
+		cmd.Flags().StringVar(&ooID, "token-id", "", "Token ID")
+		tokCmd.AddCommand(cmd)
+	}
+
+	// balance
+	var tbAddr string
+	{
+		cmd := &cobra.Command{Use: "balance", Short: "Number of NFTs for address", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if tbAddr=="" { log.Fatal("--address required") }
+			ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel()
+			c, _ := mustClient(ctx)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			bal, err := callBig(ctx, con, "balanceOf", common.HexToAddress(tbAddr)); if err != nil { log.Fatal(err) }
+			fmt.Println(bal.String())
+		}}
+		cmd.Flags().StringVar(&tbAddr, "address", "", "Address")
+		tokCmd.AddCommand(cmd)
+	}
+
+	// token-uri
+	var tuID string
+	{
+		cmd := &cobra.Command{Use: "token-uri", Short: "Get tokenURI", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if tuID=="" { log.Fatal("--token-id required") }
+			id, ok := new(big.Int).SetString(tuID, 10); if !ok { log.Fatal("invalid token-id") }
+			ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel()
+			c, _ := mustClient(ctx)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			uri, err := callString(ctx, con, "tokenURI", id); if err != nil { log.Fatal(err) }
+			fmt.Println(uri)
+		}}
+		cmd.Flags().StringVar(&tuID, "token-id", "", "Token ID")
+		tokCmd.AddCommand(cmd)
+	}
+
+	// doc-hash
+	var dhID string
+	{
+		cmd := &cobra.Command{Use: "doc-hash", Short: "Get document hash", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if dhID=="" { log.Fatal("--token-id required") }
+			id, ok := new(big.Int).SetString(dhID, 10); if !ok { log.Fatal("invalid token-id") }
+			ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel()
+			c, _ := mustClient(ctx)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			h, err := callBytes32(ctx, con, "documentHashOf", id); if err != nil { log.Fatal(err) }
+			fmt.Println("0x" + common.Bytes2Hex(h[:]))
+		}}
+		cmd.Flags().StringVar(&dhID, "token-id", "", "Token ID")
+		tokCmd.AddCommand(cmd)
+	}
+
+	// appraisal
+	var apID string
+	{
+		cmd := &cobra.Command{Use: "appraisal", Short: "Get appraisal value (base units)", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if apID=="" { log.Fatal("--token-id required") }
+			id, ok := new(big.Int).SetString(apID, 10); if !ok { log.Fatal("invalid token-id") }
+			ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel()
+			c, _ := mustClient(ctx)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			v, err := callBig(ctx, con, "appraisalOf", id); if err != nil { log.Fatal(err) }
+			fmt.Println(v.String())
+		}}
+		cmd.Flags().StringVar(&apID, "token-id", "", "Token ID")
+		tokCmd.AddCommand(cmd)
+	}
+
+	// set-token-uri
+	var stuID, stuURI string
+	{
+		cmd := &cobra.Command{Use: "set-token-uri", Short: "Set token URI (METADATA_ROLE)", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if stuID==""||stuURI=="" { log.Fatal("--token-id --uri required") }
+			id, ok := new(big.Int).SetString(stuID, 10); if !ok { log.Fatal("invalid token-id") }
+			ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second); defer cancel()
+			c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			tx, err := con.Transact(auth, "setTokenURI", id, stuURI); if err != nil { log.Fatal(err) }
+			fmt.Println(tx.Hash().Hex())
+		}}
+		cmd.Flags().StringVar(&stuID, "token-id", "", "Token ID")
+		cmd.Flags().StringVar(&stuURI, "uri", "", "New URI")
+		tokCmd.AddCommand(cmd)
+	}
+
+	// set-appraisal
+	var sapID, sapVal string
+	{
+		cmd := &cobra.Command{Use: "set-appraisal", Short: "Set appraisal value (base units)", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if sapID==""||sapVal=="" { log.Fatal("--token-id --value required") }
+			id, ok := new(big.Int).SetString(sapID, 10); if !ok { log.Fatal("invalid token-id") }
+			val, err := toWei(sapVal); if err != nil { log.Fatal(err) }
+			ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second); defer cancel()
+			c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			tx, err := con.Transact(auth, "setAppraisal", id, val); if err != nil { log.Fatal(err) }
+			fmt.Println(tx.Hash().Hex())
+		}}
+		cmd.Flags().StringVar(&sapID, "token-id", "", "Token ID")
+		cmd.Flags().StringVar(&sapVal, "value", "", "Appraisal in base units")
+		tokCmd.AddCommand(cmd)
+	}
+
+	// safe-mint
+	var smTo, smURI, smHash, smVal string
+	{
+		cmd := &cobra.Command{Use: "safe-mint", Short: "Mint new document NFT", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if smTo==""||smURI==""||smHash==""||smVal=="" { log.Fatal("--to --uri --hash --value required") }
+			h, err := fromHex32(smHash); if err != nil { log.Fatal(err) }
+			v, err := toWei(smVal); if err != nil { log.Fatal(err) }
+			ctx, cancel := context.WithTimeout(context.Background(), 240*time.Second); defer cancel()
+			c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			tx, err := con.Transact(auth, "safeMint", common.HexToAddress(smTo), smURI, h, v); if err != nil { log.Fatal(err) }
+			fmt.Println(tx.Hash().Hex())
+		}}
+		cmd.Flags().StringVar(&smTo, "to", "", "Recipient address")
+		cmd.Flags().StringVar(&smURI, "uri", "", "Document URL")
+		cmd.Flags().StringVar(&smHash, "hash", "", "Document hash (0x + 64 hex)")
+		cmd.Flags().StringVar(&smVal, "value", "", "Appraisal in base units")
+		tokCmd.AddCommand(cmd)
+	}
+
+	// transfer & safe-transfer
+	var ttTo, ttID string
+	{
+		cmd := &cobra.Command{Use: "transfer", Short: "transferFrom(sender,to,tokenId)", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if ttTo==""||ttID=="" { log.Fatal("--to --token-id required") }
+			id, ok := new(big.Int).SetString(ttID, 10); if !ok { log.Fatal("invalid token-id") }
+			ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second); defer cancel()
+			c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+			sender := crypto.PubkeyToAddress(mustPrivKey().PublicKey)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			tx, err := con.Transact(auth, "safeTransferFrom", sender, common.HexToAddress(ttTo), id); if err != nil { log.Fatal(err) }
+			fmt.Println(tx.Hash().Hex())
+		}}
+		cmd.Flags().StringVar(&ttTo, "to", "", "Recipient address")
+		cmd.Flags().StringVar(&ttID, "token-id", "", "Token ID")
+		tokCmd.AddCommand(cmd)
+	}
+
+	var stTo, stID string
+	{
+		cmd := &cobra.Command{Use: "safe-transfer", Short: "safeTransferFrom(sender,to,tokenId)", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if stTo==""||stID=="" { log.Fatal("--to --token-id required") }
+			id, ok := new(big.Int).SetString(stID, 10); if !ok { log.Fatal("invalid token-id") }
+			ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second); defer cancel()
+			c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+			sender := crypto.PubkeyToAddress(mustPrivKey().PublicKey)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			tx, err := con.Transact(auth, "safeTransferFrom", sender, common.HexToAddress(stTo), id); if err != nil { log.Fatal(err) }
+			fmt.Println(tx.Hash().Hex())
+		}}
+		cmd.Flags().StringVar(&stTo, "to", "", "Recipient address")
+		cmd.Flags().StringVar(&stID, "token-id", "", "Token ID")
+		tokCmd.AddCommand(cmd)
+	}
+
+	// approvals
+	var apTo string; var apID2 string
+	{
+		cmd := &cobra.Command{Use: "approve", Short: "Approve address for tokenId", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if apTo==""||apID2=="" { log.Fatal("--to --token-id required") }
+			id, ok := new(big.Int).SetString(apID2, 10); if !ok { log.Fatal("invalid token-id") }
+			ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second); defer cancel()
+			c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			tx, err := con.Transact(auth, "approve", common.HexToAddress(apTo), id); if err != nil { log.Fatal(err) }
+			fmt.Println(tx.Hash().Hex())
+		}}
+		cmd.Flags().StringVar(&apTo, "to", "", "Approved address")
+		cmd.Flags().StringVar(&apID2, "token-id", "", "Token ID")
+		tokCmd.AddCommand(cmd)
+	}
+
+	var saoOp string; var saoApproved bool
+	{
+		cmd := &cobra.Command{Use: "set-approval-for-all", Short: "Set operator approval", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if saoOp=="" { log.Fatal("--operator required") }
+			ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second); defer cancel()
+			c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			tx, err := con.Transact(auth, "setApprovalForAll", common.HexToAddress(saoOp), saoApproved); if err != nil { log.Fatal(err) }
+			fmt.Println(tx.Hash().Hex())
+		}}
+		cmd.Flags().StringVar(&saoOp, "operator", "", "Operator address")
+		cmd.Flags().BoolVar(&saoApproved, "approved", false, "Approved status")
+		tokCmd.AddCommand(cmd)
+	}
+
+	// getters for approvals
+	var gaID string
+	{
+		cmd := &cobra.Command{Use: "get-approved", Short: "Get approved for tokenId", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if gaID=="" { log.Fatal("--token-id required") }
+			id, ok := new(big.Int).SetString(gaID, 10); if !ok { log.Fatal("invalid token-id") }
+			ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel()
+			c, _ := mustClient(ctx)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			a, err := callAddress(ctx, con, "getApproved", id); if err != nil { log.Fatal(err) }
+			fmt.Println(a.Hex())
+		}}
+		cmd.Flags().StringVar(&gaID, "token-id", "", "Token ID")
+		tokCmd.AddCommand(cmd)
+	}
+
+	var iaOwner, iaOp string
+	{
+		cmd := &cobra.Command{Use: "is-approved-for-all", Short: "Is operator approved for owner", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if iaOwner==""||iaOp=="" { log.Fatal("--owner --operator required") }
+			ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel()
+			c, _ := mustClient(ctx)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			v, err := callBool(ctx, con, "isApprovedForAll", common.HexToAddress(iaOwner), common.HexToAddress(iaOp)); if err != nil { log.Fatal(err) }
+			fmt.Println(v)
+		}}
+		cmd.Flags().StringVar(&iaOwner, "owner", "", "Owner address")
+		cmd.Flags().StringVar(&iaOp, "operator", "", "Operator address")
+		tokCmd.AddCommand(cmd)
+	}
+
+	// pause/unpause/paused
+	tokCmd.AddCommand(&cobra.Command{Use: "paused", Short: "Is paused?", Run: func(cmd *cobra.Command, args []string) {
+		loadEnv(); ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel()
+		c, _ := mustClient(ctx)
+		addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+		con := boundContract(addr, easyTokenDocumentABI, c)
+		p, err := callBool(ctx, con, "paused"); if err != nil { log.Fatal(err) }
+		fmt.Println(p)
+	}})
+
+	tokCmd.AddCommand(&cobra.Command{Use: "pause", Short: "Pause transfers", Run: func(cmd *cobra.Command, args []string) {
+		loadEnv(); ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second); defer cancel()
+		c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+		addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+		con := boundContract(addr, easyTokenDocumentABI, c)
+		tx, err := con.Transact(auth, "pause"); if err != nil { log.Fatal(err) }
+		fmt.Println(tx.Hash().Hex())
+	}})
+
+	tokCmd.AddCommand(&cobra.Command{Use: "unpause", Short: "Unpause transfers", Run: func(cmd *cobra.Command, args []string) {
+		loadEnv(); ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second); defer cancel()
+		c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+		addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+		con := boundContract(addr, easyTokenDocumentABI, c)
+		tx, err := con.Transact(auth, "unpause"); if err != nil { log.Fatal(err) }
+		fmt.Println(tx.Hash().Hex())
+	}})
+
+	// blacklist
+	var tblAcc string; var tblStatus bool
+	{
+		cmd := &cobra.Command{Use: "set-blacklist", Short: "Set blacklist status", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if tblAcc=="" { log.Fatal("--account required") }
+			ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second); defer cancel()
+			c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			tx, err := con.Transact(auth, "setBlacklist", common.HexToAddress(tblAcc), tblStatus); if err != nil { log.Fatal(err) }
+			fmt.Println(tx.Hash().Hex())
+		}}
+		cmd.Flags().StringVar(&tblAcc, "account", "", "Account")
+		cmd.Flags().BoolVar(&tblStatus, "status", false, "Blacklist status")
+		tokCmd.AddCommand(cmd)
+	}
+
+	var tqblAcc string
+	{
+		cmd := &cobra.Command{Use: "is-blacklisted", Short: "Check blacklist", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if tqblAcc=="" { log.Fatal("--account required") }
+			ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel()
+			c, _ := mustClient(ctx)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			v, err := callBool(ctx, con, "isBlacklisted", common.HexToAddress(tqblAcc)); if err != nil { log.Fatal(err) }
+			fmt.Println(v)
+		}}
+		cmd.Flags().StringVar(&tqblAcc, "account", "", "Account")
+		tokCmd.AddCommand(cmd)
+	}
+
+	// roles
+	var trRole, trAcc string
+	{
+		cmd := &cobra.Command{Use: "grant-role", Short: "Grant role", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if trRole==""||trAcc=="" { log.Fatal("--role --account required") }
+			ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second); defer cancel()
+			c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			role := roleID(trRole)
+			tx, err := con.Transact(auth, "grantRole", role, common.HexToAddress(trAcc)); if err != nil { log.Fatal(err) }
+			fmt.Println(tx.Hash().Hex())
+		}}
+		cmd.Flags().StringVar(&trRole, "role", "", "Role name (DEFAULT_ADMIN_ROLE, PAUSER_ROLE, MINTER_ROLE, METADATA_ROLE, APPRAISER_ROLE, COMPLIANCE_ROLE)")
+		cmd.Flags().StringVar(&trAcc, "account", "", "Account address")
+		tokCmd.AddCommand(cmd)
+	}
+
+	trRole, trAcc = "", ""
+	{
+		cmd := &cobra.Command{Use: "revoke-role", Short: "Revoke role", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if trRole==""||trAcc=="" { log.Fatal("--role --account required") }
+			ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second); defer cancel()
+			c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			role := roleID(trRole)
+			tx, err := con.Transact(auth, "revokeRole", role, common.HexToAddress(trAcc)); if err != nil { log.Fatal(err) }
+			fmt.Println(tx.Hash().Hex())
+		}}
+		cmd.Flags().StringVar(&trRole, "role", "", "Role name")
+		cmd.Flags().StringVar(&trAcc, "account", "", "Account address")
+		tokCmd.AddCommand(cmd)
+	}
+
+	trRole, trAcc = "", ""
+	{
+		cmd := &cobra.Command{Use: "has-role", Short: "Has role?", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if trRole==""||trAcc=="" { log.Fatal("--role --account required") }
+			ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel()
+			c, _ := mustClient(ctx)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			role := roleID(trRole)
+			v, err := callBool(ctx, con, "hasRole", role, common.HexToAddress(trAcc)); if err != nil { log.Fatal(err) }
+			fmt.Println(v)
+		}}
+		cmd.Flags().StringVar(&trRole, "role", "", "Role name")
+		cmd.Flags().StringVar(&trAcc, "account", "", "Account address")
+		tokCmd.AddCommand(cmd)
+	}
+
+	var tgcAcc string
+	{
+		cmd := &cobra.Command{Use: "grant-compliance-role", Short: "Grant COMPLIANCE_ROLE via helper", Run: func(cmd *cobra.Command, args []string) {
+			loadEnv(); if tgcAcc=="" { log.Fatal("--account required") }
+			ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second); defer cancel()
+			c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			tx, err := con.Transact(auth, "grantComplianceRole", common.HexToAddress(tgcAcc)); if err != nil { log.Fatal(err) }
+			fmt.Println(tx.Hash().Hex())
+		}}
+		cmd.Flags().StringVar(&tgcAcc, "account", "", "Account address")
+		tokCmd.AddCommand(cmd)
+	}
+
+	return tokCmd
+}
+
+func cmdPolygon() *cobra.Command {
+	pg := &cobra.Command{Use: "polygon", Short: "Polygon utilities"}
+	pg.AddCommand(&cobra.Command{Use: "create-new-address", Short: "Generate a new wallet", Run: func(cmd *cobra.Command, args []string) {
+		pk, err := crypto.GenerateKey(); if err != nil { log.Fatal(err) }
+		pkHex := "0x" + common.Bytes2Hex(crypto.FromECDSA(pk))
+		pubHex := "0x" + common.Bytes2Hex(crypto.FromECDSAPub(&pk.PublicKey))
+		addr := crypto.PubkeyToAddress(pk.PublicKey).Hex()
+		fmt.Printf("privateKey=%s\npublicKey=%s\naddress=%s\n", pkHex, pubHex, addr)
+	}})
+	return pg
+}
+
+func main() {
+	root := &cobra.Command{Use: "sdk", Short: "CLI for EasyBRL (coin) and EasyToken (token)"}
+	root.PersistentFlags().StringVar(&rpcURL, "rpc", "", "RPC URL (overrides .env)")
+	root.PersistentFlags().StringVar(&pkHex, "pk", "", "Private key hex (overrides .env)")
+	root.PersistentFlags().StringVar(&coinAddr, "coin-addr", coinAddr, "ERC20 contract address (overrides .env)")
+	root.PersistentFlags().StringVar(&tokenAddr, "token-addr", tokenAddr, "ERC721 contract address (overrides .env)")
+
+	root.AddCommand(cmdCoin())
+	root.AddCommand(cmdToken())
+	root.AddCommand(cmdPolygon())
+
+	if err := root.Execute(); err != nil {
+		os.Exit(1)
+	}
+}

+ 13 - 0
tsconfig.json

@@ -0,0 +1,13 @@
+/* Based on https://github.com/tsconfig/bases/blob/501da2bcd640cf95c95805783e1012b992338f28/bases/node22.json */
+{
+  "compilerOptions": {
+    "lib": ["es2023"],
+    "module": "node16",
+    "target": "es2022",
+    "strict": true,
+    "esModuleInterop": true,
+    "skipLibCheck": true,
+    "moduleResolution": "node16",
+    "outDir": "dist"
+  }
+}

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