@meantjs/profile 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,44 @@
2
2
 
3
3
  The five Candid Profile library packages (`@meantjs/profile`, `/format`, `/consent`, `/tools`, `/reconcile`) release together under one version and pin each other exactly; a version appears here even when this package did not change. Release steps: `docs/releasing.md` in the repository.
4
4
 
5
+ ## 0.3.0 — 2026-10-29
6
+
7
+ - EIP-712 typed messages, experimental (spec §15.9, D-055). A contract profile or EVM pack may carry
8
+ `binds.eip712: { domain?, types }`, and a contract profile may have it instead of `binds.abi`. A message is a type
9
+ the same document tags with `intent`, addressed like a function (`PermitSingle(0).details.amount`); the resolved form
10
+ lists messages under a new `messages` map (`encode_type`, `type_hash`, `tags`, `args`) and the bound domain as
11
+ `profiles[].eip712_domain`. Without types, the resolved form and the interface id are unchanged.
12
+ - `amount` takes `token`, a relative path to the `address` field that names its token (EVM only); `timestamp` takes
13
+ `threshold` with `message`, and `labels` (display only). New errors `E_EIP712` and `E_TOKEN_PATH`.
14
+ - `@meantjs/profile/evm` exports `encodeType`, `eip712TypeHash` and `parseEip712Types`; `abiInterfaceId` takes the
15
+ struct types as a second argument.
16
+ - New standard files: the `erc2612` pack (in `STANDARD_PACKS` and `loadPacks()`), and the `permit2` and `dai-permit`
17
+ contract profiles, shipped beside the packs (`@meantjs/profile/packs/permit2.profile.json`); `loadPacks()` returns
18
+ packs only, as before.
19
+
20
+ ## 0.2.0 — 2026-10-28
21
+
22
+ - `intent` and `effect` may sit on a case of a variant in a method's arguments, such as `propose(0).Transfer`
23
+ (spec §5.2, D-053). The resolved form carries them on the case node. New errors: `E_CASE_OVERLAP` (two tagged cases
24
+ of one method that one call can hold both of) and `E_CASE_EFFECT` (a case effect that needs confirmation, or is
25
+ irreversible, under a method effect that does not or is not); `E_TAG_TARGET` for a case tag at any other value
26
+ address; `E_TEMPLATE_PATH` for a case template's placeholder through another case of a variant on its path. A
27
+ profile that 0.1.0 accepts resolves as before.
28
+ - EVM contracts, experimental (spec §15, D-054). `kind: "contract"` profiles carry an ABI (`binds.abi`, human-readable
29
+ fragments or JSON entries) and optional `binds.deployments`; EVM packs carry `binds.abi` and apply by selector, with
30
+ their addresses and placeholders rewritten to the contract's parameter names. `resolveService({ abi })` and
31
+ `candid-profile resolve --abi <file>` resolve a contract; its resolved form has `"chain": "evm"`, `abi` nodes, and
32
+ each method's `selector`, `signature` and `state_mutability`. A new standard pack, `erc20`; `loadPacks()` and
33
+ `STANDARD_PACKS` now list it first, and a Candid service skips it. New errors: `E_ABI`, `E_CHAIN`,
34
+ `E_ADDRESS_CHECKSUM`; on a contract, `E_TAG_TARGET` for `idempotency`, `outcome`, `list`, `names` and `named`,
35
+ `E_TAG_NOT_APPLICABLE` for `account` and `subaccount`, and `E_TEMPLATE` for `[ ]` segments.
36
+ - `@meantjs/profile/evm`, browser-safe and without dependencies: Keccak-256, ABI fragments (`parseAbi`,
37
+ `parseSignature`, canonical text, selectors, `abiInterfaceId`), a strict ABI codec (`encodeParams`, `encodeCall`,
38
+ `decodeParams`, which refuses anything but the canonical encoding), EIP-55 addresses (`toChecksumAddress`,
39
+ `readAddress`) and EIP-712 digests (`typedDataDigest`).
40
+ - The schema: `amount` takes `threshold` and `message` (display only: a value at or above the threshold shows as the
41
+ message and the symbol, "Unlimited USDC").
42
+
5
43
  ## 0.1.0 — 2026-10-28
6
44
 
7
45
  First published version.
package/README.md CHANGED
@@ -10,7 +10,8 @@ npm install @meantjs/profile
10
10
 
11
11
  - **Runs on:** Node 22 or later for the main entry and the `candid-profile` CLI. `@meantjs/profile/browser` is for
12
12
  browser bundles (checked with esbuild, and in Chromium under a CSP without `'unsafe-eval'`; the cli's wasm needs
13
- `'wasm-unsafe-eval'`, below). `@meantjs/profile/address` has no dependency and runs anywhere.
13
+ `'wasm-unsafe-eval'`, below). `@meantjs/profile/address` and `@meantjs/profile/evm` have no dependency and run
14
+ anywhere.
14
15
  - **Module format:** ES modules only, with hand-written TypeScript declarations for every entry.
15
16
  - **Spec:** [Candid Profile v0](https://github.com/B3Pay/Meant/blob/main/profile/docs/profile-v0.md). **Licence:** Apache-2.0.
16
17
  - **Versions:** the five Candid Profile packages (`@meantjs/profile`, `/format`, `/consent`, `/tools`,
@@ -21,7 +22,7 @@ npm install @meantjs/profile
21
22
  import { createSchemaValidator, resolveService, loadPacks, validateProfile } from "@meantjs/profile";
22
23
 
23
24
  const validateSchema = await createSchemaValidator(); // the schema this package ships
24
- const packs = await loadPacks(); // the standard packs it ships (ICRC-1, ICRC-2)
25
+ const packs = await loadPacks(); // the standard packs it ships (ERC-20, ICRC-1, ICRC-2)
25
26
  const r = await resolveService({
26
27
  did: await readFile("ledger.did", "utf8"),
27
28
  validateSchema,
@@ -163,10 +164,46 @@ const r = await resolveService({ did: serviceText, profiles: served.ok ? [{ doc:
163
164
  serves, so a profile for another interface is `rebound` or `stale` as any other.
164
165
  - Tests: `test/served.test.mjs`; end to end on PocketIC in `packages/kit/pocketic/`.
165
166
 
167
+ ## EVM contracts (experimental, spec section 15, D-054 and D-055)
168
+
169
+ A third input, `abi`: an EVM contract's ABI as human-readable fragments or JSON ABI entries. The resolver reads it as
170
+ the graph a `.did` compiles to (a function is a method, its parameters one record at argument 0, a tuple a record in
171
+ ABI order), so addresses, tags, layers and the resolved form work unchanged; the resolved form says `"chain": "evm"`,
172
+ its nodes carry `abi` instead of `candid`, and each method has `selector`, `signature` and `state_mutability`.
173
+
174
+ ```js
175
+ const r = await resolveService({ abi: ["function transfer(address dst, uint256 wad) returns (bool)", …], validateSchema, packs: (await loadPacks()).map(({ doc }) => ({ doc })) });
176
+ r.resolved.methods.transfer.tags.intent.args.template; // "Send {wad} to {dst}": the erc20 pack's, in this contract's names
177
+ ```
178
+
179
+ - **Contract profiles** (`kind: "contract"`) carry their ABI in `binds.abi` and the contracts they describe in
180
+ `binds.deployments` (EIP-55 checked: `E_ADDRESS_CHECKSUM`). **EVM packs** carry `binds.abi` and apply to a contract
181
+ that has every one of their functions, by selector, output types and kind of call (no subtyping); their addresses
182
+ and placeholders are rewritten to the contract's parameter names by position. A pack or profile for the other kind
183
+ of interface is `E_CHAIN` when given, and skipped in a pack search, so `loadPacks()` serves both kinds.
184
+ - **EIP-712 messages** (spec §15.9, D-055): `binds.eip712: { domain?, types }` in a contract profile (with or without
185
+ `binds.abi`) or an EVM pack. A type the same document tags with `intent` is a message, addressed like a function
186
+ (`PermitSingle(0).details.amount`) and listed under the resolved form's `messages` with its `encode_type` and
187
+ `type_hash`. `amount.token` names an amount's token by an `address` field (`E_TOKEN_PATH` otherwise). Bad types, a
188
+ message named like a function and two meanings for one type name are `E_EIP712`. Standard files: the `erc2612`
189
+ pack, and the `permit2` and `dai-permit` contract profiles shipped beside the packs
190
+ (`@meantjs/profile/packs/permit2.profile.json`); `loadPacks()` returns the packs only.
191
+ - **Refused for now:** overloaded function names and unreadable fragments (`E_ABI`); `idempotency`, `outcome`, `list`,
192
+ `names` and `named` on a contract (`E_TAG_TARGET`); `account` and `subaccount` (`E_TAG_NOT_APPLICABLE`); `[ ]`
193
+ template segments (`E_TEMPLATE`).
194
+ - **`@meantjs/profile/evm`** is the toolkit underneath, browser-safe and without dependencies: `keccak256`, `parseAbi`,
195
+ `parseSignature`, `canonicalFragment`, `selectorOf`, `abiInterfaceId`, a strict codec (`encodeParams`, `encodeCall`,
196
+ `decodeParams`: a value read from anything but its canonical encoding is refused), `toChecksumAddress`,
197
+ `readAddress`, `typedDataDigest` (EIP-712), `encodeType`, `eip712TypeHash` and `parseEip712Types`.
198
+ `test/evm.test.mjs` checks Keccak against an independent implementation (and the block boundary), the ERC-20
199
+ selectors, the ERC-8176 schema UID, EIP-55's examples and EIP-712's Mail example; `test/eip712.test.mjs` the type
200
+ hashes of the standard messages (DAI's equals `dai.sol`'s constant).
201
+
166
202
  ## CLI
167
203
 
168
204
  ```sh
169
205
  node packages/resolver/bin/candid-profile.mjs resolve <service.did> [--profile p.json ...] [--packs] [--packs-dir dir] [--verbose] [--quiet] > resolved.json
206
+ node packages/resolver/bin/candid-profile.mjs resolve --abi contract.abi.json [--profile p.json ...] [--packs] # an EVM contract (section 15)
170
207
  # after `npm ci` the bin is also linked: npx candid-profile resolve ...
171
208
  ```
172
209
 
@@ -0,0 +1,113 @@
1
+ {
2
+ "format": "candid-profile",
3
+ "format_version": 0,
4
+ "kind": "contract",
5
+ "name": "dai-permit",
6
+ "origin": "third_party",
7
+ "language": "en",
8
+ "description": "DAI's permit on Ethereum mainnet (spec section 15.9, experimental), older than EIP-2612 and different from it: `allowed` sets the allowance to all of the holder's DAI (true, uint256 max) or to none (false), and `expiry` 0 means the signature never expires (MakerDAO dss, src/dai.sol). Only the message is described; DAI's functions are ERC-20's and the erc20 pack covers them.",
9
+ "binds": {
10
+ "eip712": {
11
+ "domain": {
12
+ "name": "Dai Stablecoin",
13
+ "version": "1"
14
+ },
15
+ "types": {
16
+ "Permit": [
17
+ {
18
+ "name": "holder",
19
+ "type": "address"
20
+ },
21
+ {
22
+ "name": "spender",
23
+ "type": "address"
24
+ },
25
+ {
26
+ "name": "nonce",
27
+ "type": "uint256"
28
+ },
29
+ {
30
+ "name": "expiry",
31
+ "type": "uint256"
32
+ },
33
+ {
34
+ "name": "allowed",
35
+ "type": "bool"
36
+ }
37
+ ]
38
+ }
39
+ },
40
+ "deployments": [
41
+ {
42
+ "chain_id": 1,
43
+ "address": "0x6B175474E89094C44Da98b954EedeAC495271d0F"
44
+ }
45
+ ]
46
+ },
47
+ "annotations": [
48
+ {
49
+ "at": "Permit",
50
+ "tag": "intent",
51
+ "args": {
52
+ "title": "Permit DAI spending",
53
+ "template": "Set the DAI allowance {holder} gives {spender} to all of it if allowed is true, to none if false (allowed: {allowed}). Signature expires: {expiry}."
54
+ }
55
+ },
56
+ {
57
+ "at": "Permit",
58
+ "tag": "effect",
59
+ "args": {
60
+ "kind": "approve",
61
+ "irreversible": false
62
+ }
63
+ },
64
+ {
65
+ "at": "Permit(0).holder",
66
+ "tag": "describe",
67
+ "args": {
68
+ "label": "Holder"
69
+ }
70
+ },
71
+ {
72
+ "at": "Permit(0).spender",
73
+ "tag": "describe",
74
+ "args": {
75
+ "label": "Spender"
76
+ }
77
+ },
78
+ {
79
+ "at": "Permit(0).allowed",
80
+ "tag": "describe",
81
+ "args": {
82
+ "label": "Allowed (true: all, false: none)"
83
+ }
84
+ },
85
+ {
86
+ "at": "Permit(0).expiry",
87
+ "tag": "timestamp",
88
+ "args": {
89
+ "unit": "s",
90
+ "epoch": "unix",
91
+ "threshold": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
92
+ "message": "Never",
93
+ "labels": {
94
+ "0": "Never"
95
+ }
96
+ }
97
+ },
98
+ {
99
+ "at": "Permit(0).expiry",
100
+ "tag": "describe",
101
+ "args": {
102
+ "label": "Signature expires"
103
+ }
104
+ },
105
+ {
106
+ "at": "Permit(0).nonce",
107
+ "tag": "describe",
108
+ "args": {
109
+ "label": "Nonce"
110
+ }
111
+ }
112
+ ]
113
+ }
@@ -0,0 +1,221 @@
1
+ {
2
+ "format": "candid-profile",
3
+ "format_version": 0,
4
+ "kind": "pack",
5
+ "name": "erc20",
6
+ "origin": "pack",
7
+ "language": "en",
8
+ "description": "Standard pack for EIP-20 tokens on EVM chains (spec section 15, experimental). Applies to any contract that has every function of `binds.abi` with the same selector, outputs and kind of call; parameter names may differ (they are matched by position). `name()` is left out: no annotation reads it and EIP-20 makes it optional. A token without `decimals()` or `symbol()` matches no pack and needs a contract profile with literal values.",
9
+ "binds": {
10
+ "standard": "ERC-20",
11
+ "spec_url": "https://eips.ethereum.org/EIPS/eip-20",
12
+ "abi": [
13
+ "function symbol() view returns (string)",
14
+ "function decimals() view returns (uint8)",
15
+ "function balanceOf(address owner) view returns (uint256)",
16
+ "function allowance(address owner, address spender) view returns (uint256)",
17
+ "function transfer(address to, uint256 value) returns (bool)",
18
+ "function approve(address spender, uint256 value) returns (bool)",
19
+ "function transferFrom(address from, address to, uint256 value) returns (bool)"
20
+ ]
21
+ },
22
+ "annotations": [
23
+ {
24
+ "at": "transfer",
25
+ "tag": "intent",
26
+ "args": {
27
+ "title": "Send tokens",
28
+ "template": "Send {value} to {to}"
29
+ }
30
+ },
31
+ {
32
+ "at": "transfer",
33
+ "tag": "effect",
34
+ "args": {
35
+ "kind": "transfer",
36
+ "irreversible": true
37
+ }
38
+ },
39
+ {
40
+ "at": "transfer(0).value",
41
+ "tag": "amount",
42
+ "args": {
43
+ "decimals": {
44
+ "from": "query:decimals"
45
+ },
46
+ "symbol": {
47
+ "from": "query:symbol"
48
+ }
49
+ }
50
+ },
51
+ {
52
+ "at": "transfer(0).value",
53
+ "tag": "describe",
54
+ "args": {
55
+ "label": "Amount"
56
+ }
57
+ },
58
+ {
59
+ "at": "transfer(0).to",
60
+ "tag": "describe",
61
+ "args": {
62
+ "label": "Recipient"
63
+ }
64
+ },
65
+ {
66
+ "at": "approve",
67
+ "tag": "intent",
68
+ "args": {
69
+ "title": "Approve spending",
70
+ "template": "Set {spender}'s allowance to {value}, replacing any existing allowance"
71
+ }
72
+ },
73
+ {
74
+ "at": "approve",
75
+ "tag": "effect",
76
+ "args": {
77
+ "kind": "approve",
78
+ "irreversible": false
79
+ }
80
+ },
81
+ {
82
+ "at": "approve(0).spender",
83
+ "tag": "describe",
84
+ "args": {
85
+ "label": "Spender"
86
+ }
87
+ },
88
+ {
89
+ "at": "approve(0).value",
90
+ "tag": "amount",
91
+ "args": {
92
+ "decimals": {
93
+ "from": "query:decimals"
94
+ },
95
+ "symbol": {
96
+ "from": "query:symbol"
97
+ },
98
+ "threshold": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
99
+ "message": "Unlimited"
100
+ }
101
+ },
102
+ {
103
+ "at": "approve(0).value",
104
+ "tag": "describe",
105
+ "args": {
106
+ "label": "Allowance",
107
+ "help": "Maximum the spender may transfer. An approval replaces the previous allowance; it does not add to it."
108
+ }
109
+ },
110
+ {
111
+ "at": "transferFrom",
112
+ "tag": "intent",
113
+ "args": {
114
+ "title": "Transfer using allowance",
115
+ "template": "Move {value} from {from} to {to} using an allowance"
116
+ }
117
+ },
118
+ {
119
+ "at": "transferFrom",
120
+ "tag": "effect",
121
+ "args": {
122
+ "kind": "transfer",
123
+ "irreversible": true
124
+ }
125
+ },
126
+ {
127
+ "at": "transferFrom(0).from",
128
+ "tag": "describe",
129
+ "args": {
130
+ "label": "From"
131
+ }
132
+ },
133
+ {
134
+ "at": "transferFrom(0).to",
135
+ "tag": "describe",
136
+ "args": {
137
+ "label": "Recipient"
138
+ }
139
+ },
140
+ {
141
+ "at": "transferFrom(0).value",
142
+ "tag": "amount",
143
+ "args": {
144
+ "decimals": {
145
+ "from": "query:decimals"
146
+ },
147
+ "symbol": {
148
+ "from": "query:symbol"
149
+ }
150
+ }
151
+ },
152
+ {
153
+ "at": "transferFrom(0).value",
154
+ "tag": "describe",
155
+ "args": {
156
+ "label": "Amount"
157
+ }
158
+ },
159
+ {
160
+ "at": "balanceOf",
161
+ "tag": "describe",
162
+ "args": {
163
+ "label": "Balance of an address"
164
+ }
165
+ },
166
+ {
167
+ "at": "balanceOf(0).owner",
168
+ "tag": "describe",
169
+ "args": {
170
+ "label": "Owner"
171
+ }
172
+ },
173
+ {
174
+ "at": "balanceOf->(0)",
175
+ "tag": "amount",
176
+ "args": {
177
+ "decimals": {
178
+ "from": "query:decimals"
179
+ },
180
+ "symbol": {
181
+ "from": "query:symbol"
182
+ }
183
+ }
184
+ },
185
+ {
186
+ "at": "allowance",
187
+ "tag": "describe",
188
+ "args": {
189
+ "label": "Allowance of a spender"
190
+ }
191
+ },
192
+ {
193
+ "at": "allowance(0).owner",
194
+ "tag": "describe",
195
+ "args": {
196
+ "label": "Owner"
197
+ }
198
+ },
199
+ {
200
+ "at": "allowance(0).spender",
201
+ "tag": "describe",
202
+ "args": {
203
+ "label": "Spender"
204
+ }
205
+ },
206
+ {
207
+ "at": "allowance->(0)",
208
+ "tag": "amount",
209
+ "args": {
210
+ "decimals": {
211
+ "from": "query:decimals"
212
+ },
213
+ "symbol": {
214
+ "from": "query:symbol"
215
+ },
216
+ "threshold": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
217
+ "message": "Unlimited"
218
+ }
219
+ }
220
+ ]
221
+ }
@@ -0,0 +1,196 @@
1
+ {
2
+ "format": "candid-profile",
3
+ "format_version": 0,
4
+ "kind": "pack",
5
+ "name": "erc2612",
6
+ "origin": "pack",
7
+ "language": "en",
8
+ "description": "Standard pack for EIP-2612 permits on EVM chains (spec section 15.9, experimental): the signed `Permit` message that sets an ERC-20 allowance, and the `permit` function that submits one. Applies to a contract that has every function of `binds.abi` with the same selector, outputs and kind of call, `decimals()` and `symbol()` included, since the amounts read them. A signing request matches the `Permit` message by its EIP-712 type hash, and its verifying contract is the token, so `query:` refs are calls to that token. The domain's name and version are each token's own, so the pack names neither.",
9
+ "binds": {
10
+ "standard": "ERC-2612",
11
+ "spec_url": "https://eips.ethereum.org/EIPS/eip-2612",
12
+ "abi": [
13
+ "function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)",
14
+ "function nonces(address owner) view returns (uint256)",
15
+ "function DOMAIN_SEPARATOR() view returns (bytes32)",
16
+ "function decimals() view returns (uint8)",
17
+ "function symbol() view returns (string)"
18
+ ],
19
+ "eip712": {
20
+ "types": {
21
+ "Permit": [
22
+ {
23
+ "name": "owner",
24
+ "type": "address"
25
+ },
26
+ {
27
+ "name": "spender",
28
+ "type": "address"
29
+ },
30
+ {
31
+ "name": "value",
32
+ "type": "uint256"
33
+ },
34
+ {
35
+ "name": "nonce",
36
+ "type": "uint256"
37
+ },
38
+ {
39
+ "name": "deadline",
40
+ "type": "uint256"
41
+ }
42
+ ]
43
+ }
44
+ }
45
+ },
46
+ "annotations": [
47
+ {
48
+ "at": "Permit",
49
+ "tag": "intent",
50
+ "args": {
51
+ "title": "Permit token spending",
52
+ "template": "Allow {spender} to spend up to {value} from {owner}. Signature expires: {deadline}."
53
+ }
54
+ },
55
+ {
56
+ "at": "Permit",
57
+ "tag": "effect",
58
+ "args": {
59
+ "kind": "approve",
60
+ "irreversible": false
61
+ }
62
+ },
63
+ {
64
+ "at": "Permit(0).value",
65
+ "tag": "amount",
66
+ "args": {
67
+ "decimals": {
68
+ "from": "query:decimals"
69
+ },
70
+ "symbol": {
71
+ "from": "query:symbol"
72
+ },
73
+ "threshold": "115792089237316195423570985008687907853269984665640564039457584007913129639935"
74
+ }
75
+ },
76
+ {
77
+ "at": "Permit(0).value",
78
+ "tag": "describe",
79
+ "args": {
80
+ "label": "Amount"
81
+ }
82
+ },
83
+ {
84
+ "at": "Permit(0).deadline",
85
+ "tag": "timestamp",
86
+ "args": {
87
+ "unit": "s",
88
+ "epoch": "unix",
89
+ "threshold": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
90
+ "message": "Never"
91
+ }
92
+ },
93
+ {
94
+ "at": "Permit(0).deadline",
95
+ "tag": "describe",
96
+ "args": {
97
+ "label": "Signature expires"
98
+ }
99
+ },
100
+ {
101
+ "at": "Permit(0).owner",
102
+ "tag": "describe",
103
+ "args": {
104
+ "label": "Owner"
105
+ }
106
+ },
107
+ {
108
+ "at": "Permit(0).spender",
109
+ "tag": "describe",
110
+ "args": {
111
+ "label": "Spender"
112
+ }
113
+ },
114
+ {
115
+ "at": "Permit(0).nonce",
116
+ "tag": "describe",
117
+ "args": {
118
+ "label": "Nonce"
119
+ }
120
+ },
121
+ {
122
+ "at": "permit",
123
+ "tag": "intent",
124
+ "args": {
125
+ "title": "Submit a token permit",
126
+ "template": "Set {spender}'s allowance from {owner} to {value} with {owner}'s signature. Signature expires: {deadline}."
127
+ }
128
+ },
129
+ {
130
+ "at": "permit",
131
+ "tag": "effect",
132
+ "args": {
133
+ "kind": "approve",
134
+ "irreversible": false
135
+ }
136
+ },
137
+ {
138
+ "at": "permit(0).value",
139
+ "tag": "amount",
140
+ "args": {
141
+ "decimals": {
142
+ "from": "query:decimals"
143
+ },
144
+ "symbol": {
145
+ "from": "query:symbol"
146
+ },
147
+ "threshold": "115792089237316195423570985008687907853269984665640564039457584007913129639935"
148
+ }
149
+ },
150
+ {
151
+ "at": "permit(0).value",
152
+ "tag": "describe",
153
+ "args": {
154
+ "label": "Amount"
155
+ }
156
+ },
157
+ {
158
+ "at": "permit(0).deadline",
159
+ "tag": "timestamp",
160
+ "args": {
161
+ "unit": "s",
162
+ "epoch": "unix",
163
+ "threshold": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
164
+ "message": "Never"
165
+ }
166
+ },
167
+ {
168
+ "at": "permit(0).deadline",
169
+ "tag": "describe",
170
+ "args": {
171
+ "label": "Signature expires"
172
+ }
173
+ },
174
+ {
175
+ "at": "permit(0).owner",
176
+ "tag": "describe",
177
+ "args": {
178
+ "label": "Owner"
179
+ }
180
+ },
181
+ {
182
+ "at": "permit(0).spender",
183
+ "tag": "describe",
184
+ "args": {
185
+ "label": "Spender"
186
+ }
187
+ },
188
+ {
189
+ "at": "permit(0).v",
190
+ "tag": "describe",
191
+ "args": {
192
+ "label": "Signature v"
193
+ }
194
+ }
195
+ ]
196
+ }