@rootzero/contracts 0.5.0 → 0.5.1
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/docs/GETTING_STARTED.md +286 -0
- package/package.json +17 -33
- package/contracts/test/TestBorrowHost.sol +0 -72
- package/contracts/test/TestBurnHost.sol +0 -31
- package/contracts/test/TestCreateHost.sol +0 -40
- package/contracts/test/TestCursorHelper.sol +0 -191
- package/contracts/test/TestDiscovery.sol +0 -9
- package/contracts/test/TestECDSA.sol +0 -19
- package/contracts/test/TestHost.sol +0 -218
- package/contracts/test/TestLiquidityHost.sol +0 -167
- package/contracts/test/TestMintHost.sol +0 -50
- package/contracts/test/TestOperation.sol +0 -21
- package/contracts/test/TestPeerHost.sol +0 -57
- package/contracts/test/TestReclaimHost.sol +0 -55
- package/contracts/test/TestRejectEther.sol +0 -11
- package/contracts/test/TestRemoveHost.sol +0 -40
- package/contracts/test/TestSupplyHost.sol +0 -25
- package/contracts/test/TestSwapHost.sol +0 -67
- package/contracts/test/TestUtils.sol +0 -184
- package/contracts/test/TestValidator.sol +0 -13
- /package/{contracts/Commands.sol → Commands.sol} +0 -0
- /package/{contracts/Core.sol → Core.sol} +0 -0
- /package/{contracts/Cursors.sol → Cursors.sol} +0 -0
- /package/{contracts/Events.sol → Events.sol} +0 -0
- /package/{contracts/Utils.sol → Utils.sol} +0 -0
- /package/{contracts/blocks → blocks}/Cursors.sol +0 -0
- /package/{contracts/blocks → blocks}/Keys.sol +0 -0
- /package/{contracts/blocks → blocks}/Schema.sol +0 -0
- /package/{contracts/blocks → blocks}/Writers.sol +0 -0
- /package/{contracts/commands → commands}/Base.sol +0 -0
- /package/{contracts/commands → commands}/Borrow.sol +0 -0
- /package/{contracts/commands → commands}/Burn.sol +0 -0
- /package/{contracts/commands → commands}/Create.sol +0 -0
- /package/{contracts/commands → commands}/Credit.sol +0 -0
- /package/{contracts/commands → commands}/Debit.sol +0 -0
- /package/{contracts/commands → commands}/Deposit.sol +0 -0
- /package/{contracts/commands → commands}/Liquidate.sol +0 -0
- /package/{contracts/commands → commands}/Liquidity.sol +0 -0
- /package/{contracts/commands → commands}/Mint.sol +0 -0
- /package/{contracts/commands → commands}/Pipe.sol +0 -0
- /package/{contracts/commands → commands}/Provision.sol +0 -0
- /package/{contracts/commands → commands}/Reclaim.sol +0 -0
- /package/{contracts/commands → commands}/Redeem.sol +0 -0
- /package/{contracts/commands → commands}/Remove.sol +0 -0
- /package/{contracts/commands → commands}/Repay.sol +0 -0
- /package/{contracts/commands → commands}/Settle.sol +0 -0
- /package/{contracts/commands → commands}/Stake.sol +0 -0
- /package/{contracts/commands → commands}/Supply.sol +0 -0
- /package/{contracts/commands → commands}/Swap.sol +0 -0
- /package/{contracts/commands → commands}/Transfer.sol +0 -0
- /package/{contracts/commands → commands}/Unstake.sol +0 -0
- /package/{contracts/commands → commands}/Withdraw.sol +0 -0
- /package/{contracts/commands → commands}/admin/Allocate.sol +0 -0
- /package/{contracts/commands → commands}/admin/AllowAssets.sol +0 -0
- /package/{contracts/commands → commands}/admin/Authorize.sol +0 -0
- /package/{contracts/commands → commands}/admin/DenyAssets.sol +0 -0
- /package/{contracts/commands → commands}/admin/Destroy.sol +0 -0
- /package/{contracts/commands → commands}/admin/Init.sol +0 -0
- /package/{contracts/commands → commands}/admin/Relocate.sol +0 -0
- /package/{contracts/commands → commands}/admin/Unauthorize.sol +0 -0
- /package/{contracts/core → core}/Access.sol +0 -0
- /package/{contracts/core → core}/Balances.sol +0 -0
- /package/{contracts/core → core}/Host.sol +0 -0
- /package/{contracts/core → core}/Operation.sol +0 -0
- /package/{contracts/core → core}/Validator.sol +0 -0
- /package/{contracts/events → events}/Access.sol +0 -0
- /package/{contracts/events → events}/Asset.sol +0 -0
- /package/{contracts/events → events}/Balance.sol +0 -0
- /package/{contracts/events → events}/Collateral.sol +0 -0
- /package/{contracts/events → events}/Command.sol +0 -0
- /package/{contracts/events → events}/Debt.sol +0 -0
- /package/{contracts/events → events}/Deposit.sol +0 -0
- /package/{contracts/events → events}/Emitter.sol +0 -0
- /package/{contracts/events → events}/Governed.sol +0 -0
- /package/{contracts/events → events}/HostAnnounced.sol +0 -0
- /package/{contracts/events → events}/Listing.sol +0 -0
- /package/{contracts/events → events}/Peer.sol +0 -0
- /package/{contracts/events → events}/Quote.sol +0 -0
- /package/{contracts/events → events}/RootZero.sol +0 -0
- /package/{contracts/events → events}/Withdraw.sol +0 -0
- /package/{contracts/interfaces → interfaces}/IHostDiscovery.sol +0 -0
- /package/{contracts/peer → peer}/AllowAssets.sol +0 -0
- /package/{contracts/peer → peer}/Base.sol +0 -0
- /package/{contracts/peer → peer}/DenyAssets.sol +0 -0
- /package/{contracts/peer → peer}/Pull.sol +0 -0
- /package/{contracts/peer → peer}/Push.sol +0 -0
- /package/{contracts/peer → peer}/Settle.sol +0 -0
- /package/{contracts/utils → utils}/Accounts.sol +0 -0
- /package/{contracts/utils → utils}/Assets.sol +0 -0
- /package/{contracts/utils → utils}/ECDSA.sol +0 -0
- /package/{contracts/utils → utils}/Ids.sol +0 -0
- /package/{contracts/utils → utils}/Layout.sol +0 -0
- /package/{contracts/utils → utils}/State.sol +0 -0
- /package/{contracts/utils → utils}/Utils.sol +0 -0
- /package/{contracts/utils → utils}/Value.sol +0 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# Getting Started With rootzero
|
|
2
|
+
|
|
3
|
+
This guide is for developers who want to build on rootzero without reading the whole codebase first.
|
|
4
|
+
|
|
5
|
+
If you remember only one thing, remember this:
|
|
6
|
+
|
|
7
|
+
- A `Host` is your application contract.
|
|
8
|
+
- A command is an entrypoint the rootzero runtime is allowed to call.
|
|
9
|
+
- Requests and responses are passed around as typed byte blocks.
|
|
10
|
+
|
|
11
|
+
## The Mental Model
|
|
12
|
+
|
|
13
|
+
rootzero moves data through a small command context:
|
|
14
|
+
|
|
15
|
+
```solidity
|
|
16
|
+
struct CommandContext {
|
|
17
|
+
uint target;
|
|
18
|
+
bytes32 account;
|
|
19
|
+
bytes state;
|
|
20
|
+
bytes request;
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
In practice:
|
|
25
|
+
|
|
26
|
+
- `account` is the user account the command is acting for.
|
|
27
|
+
- `request` is the new input for this command.
|
|
28
|
+
- `state` is data produced by an earlier command in a pipeline.
|
|
29
|
+
- `target` is the command id you expect to receive, or `0`.
|
|
30
|
+
|
|
31
|
+
Most built-in commands follow a simple pattern:
|
|
32
|
+
|
|
33
|
+
- read blocks from `request` or `state`
|
|
34
|
+
- apply your host logic
|
|
35
|
+
- return new blocks
|
|
36
|
+
|
|
37
|
+
## Step 1: Start With A Host
|
|
38
|
+
|
|
39
|
+
The smallest useful rootzero app is a host contract.
|
|
40
|
+
|
|
41
|
+
```solidity
|
|
42
|
+
// SPDX-License-Identifier: MIT
|
|
43
|
+
pragma solidity ^0.8.33;
|
|
44
|
+
|
|
45
|
+
import {Host} from "@rootzero/contracts/Core.sol";
|
|
46
|
+
|
|
47
|
+
contract ExampleHost is Host {
|
|
48
|
+
constructor(address rootzero)
|
|
49
|
+
Host(rootzero, 1, "example")
|
|
50
|
+
{}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
What the constructor arguments mean:
|
|
55
|
+
|
|
56
|
+
- `rootzero`: the trusted rootzero runtime allowed to call commands
|
|
57
|
+
- `1`: your host version
|
|
58
|
+
- `"example"`: your host namespace
|
|
59
|
+
|
|
60
|
+
If `rootzero` is a contract, the host announces itself there during deployment. If you pass `address(0)`, the host becomes self-managed and does not auto-register.
|
|
61
|
+
|
|
62
|
+
## Step 2: Reuse A Built-In Command
|
|
63
|
+
|
|
64
|
+
The easiest way to integrate is to inherit a built-in command module and implement its hook.
|
|
65
|
+
|
|
66
|
+
This example adds `DebitAccount`, which turns `AMOUNT` blocks in `request` into `BALANCE` blocks in the response:
|
|
67
|
+
|
|
68
|
+
```solidity
|
|
69
|
+
// SPDX-License-Identifier: MIT
|
|
70
|
+
pragma solidity ^0.8.33;
|
|
71
|
+
|
|
72
|
+
import {Host} from "@rootzero/contracts/Core.sol";
|
|
73
|
+
import {DebitAccount} from "@rootzero/contracts/Commands.sol";
|
|
74
|
+
import {Assets} from "@rootzero/contracts/Utils.sol";
|
|
75
|
+
|
|
76
|
+
contract ExampleHost is Host, DebitAccount {
|
|
77
|
+
mapping(bytes32 account => mapping(bytes32 assetKey => uint amount)) internal balances;
|
|
78
|
+
|
|
79
|
+
constructor(address rootzero)
|
|
80
|
+
Host(rootzero, 1, "example")
|
|
81
|
+
{}
|
|
82
|
+
|
|
83
|
+
function debitAccount(
|
|
84
|
+
bytes32 account,
|
|
85
|
+
bytes32 asset,
|
|
86
|
+
bytes32 meta,
|
|
87
|
+
uint amount
|
|
88
|
+
) internal override {
|
|
89
|
+
bytes32 key = Assets.key(asset, meta);
|
|
90
|
+
balances[account][key] -= amount;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Why this is a good first step:
|
|
96
|
+
|
|
97
|
+
- you do not need to write block parsing yourself
|
|
98
|
+
- you get the standard rootzero command surface
|
|
99
|
+
- you only implement the business rule that is unique to your app
|
|
100
|
+
|
|
101
|
+
## Step 3: Understand What Built-In Commands Consume
|
|
102
|
+
|
|
103
|
+
The built-in commands are easiest to use when you know which blocks they expect.
|
|
104
|
+
|
|
105
|
+
### Commands That Read `request`
|
|
106
|
+
|
|
107
|
+
- `deposit`: reads `AMOUNT` blocks, returns `BALANCE`
|
|
108
|
+
- `transfer`: reads `AMOUNT` plus `RECIPIENT`
|
|
109
|
+
- `debitAccount`: reads `AMOUNT`, returns `BALANCE`
|
|
110
|
+
- `provision`: reads `AMOUNT` plus `NODE`, returns `CUSTODY`
|
|
111
|
+
- `pipe`: reads `STEP` blocks and runs them in order
|
|
112
|
+
|
|
113
|
+
### Commands That Read `state`
|
|
114
|
+
|
|
115
|
+
- `withdraw`: reads `BALANCE`, optionally reads `RECIPIENT` from `request`
|
|
116
|
+
- `creditAccount`: reads `BALANCE`, optionally reads `RECIPIENT` from `request`
|
|
117
|
+
- `settle`: reads `TX`
|
|
118
|
+
- `provisionFromBalance`: reads `BALANCE` from `state` and `NODE` from `request`
|
|
119
|
+
|
|
120
|
+
This is the main pattern to keep in mind:
|
|
121
|
+
|
|
122
|
+
- use `request` for the command's direct input
|
|
123
|
+
- use `state` when a previous command already produced the input
|
|
124
|
+
|
|
125
|
+
## Step 4: Send A Simple Request
|
|
126
|
+
|
|
127
|
+
For a host that supports `deposit`, a request with one `AMOUNT` block is enough.
|
|
128
|
+
|
|
129
|
+
TypeScript helper example:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import { ethers } from "ethers";
|
|
133
|
+
import { encodeAmountBlock } from "../test/helpers/blocks.js";
|
|
134
|
+
|
|
135
|
+
const asset = ethers.zeroPadValue("0x01", 32);
|
|
136
|
+
const meta = ethers.ZeroHash;
|
|
137
|
+
const amount = 100n;
|
|
138
|
+
|
|
139
|
+
const ctx = {
|
|
140
|
+
target: 0n,
|
|
141
|
+
account: "0x...", // 32-byte rootzero account id
|
|
142
|
+
state: "0x",
|
|
143
|
+
request: encodeAmountBlock(asset, meta, amount),
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
await host.deposit(ctx);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
What happens:
|
|
150
|
+
|
|
151
|
+
1. `deposit` reads the `AMOUNT` block from `ctx.request`.
|
|
152
|
+
2. Your host applies its deposit logic.
|
|
153
|
+
3. The command returns one `BALANCE` block for each deposited amount.
|
|
154
|
+
|
|
155
|
+
## Step 5: Create A Custom Command
|
|
156
|
+
|
|
157
|
+
When the built-in modules are not enough, add your own command entrypoint.
|
|
158
|
+
|
|
159
|
+
```solidity
|
|
160
|
+
// SPDX-License-Identifier: MIT
|
|
161
|
+
pragma solidity ^0.8.33;
|
|
162
|
+
|
|
163
|
+
import {Host} from "@rootzero/contracts/Core.sol";
|
|
164
|
+
import {CommandBase, CommandContext, Channels} from "@rootzero/contracts/Commands.sol";
|
|
165
|
+
import {Cursors, Cursor, Schemas} from "@rootzero/contracts/Cursors.sol";
|
|
166
|
+
|
|
167
|
+
using Cursors for Cursor;
|
|
168
|
+
|
|
169
|
+
string constant NAME = "myCommand";
|
|
170
|
+
string constant ROUTE = "route(uint foo, uint bar)";
|
|
171
|
+
string constant INPUT = string.concat(ROUTE, "&", Schemas.Amount);
|
|
172
|
+
|
|
173
|
+
abstract contract MyCommand is CommandBase {
|
|
174
|
+
uint internal immutable myCommandId = commandId(NAME);
|
|
175
|
+
|
|
176
|
+
constructor() {
|
|
177
|
+
emit Command(host, NAME, INPUT, myCommandId, Channels.Setup, Channels.Balances);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function myCommand(
|
|
181
|
+
CommandContext calldata c
|
|
182
|
+
) external payable onlyCommand(myCommandId, c.target) returns (bytes memory) {
|
|
183
|
+
Cursor memory input = Cursors.openBlock(c.request, 0);
|
|
184
|
+
uint foo = input.unpackRouteUint();
|
|
185
|
+
(bytes32 asset, bytes32 meta, uint amount) = input.unpackAmount();
|
|
186
|
+
foo;
|
|
187
|
+
return Cursors.toBalanceBlock(asset, meta, amount);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
There are three important ideas here:
|
|
193
|
+
|
|
194
|
+
- every custom command gets a deterministic command id
|
|
195
|
+
- you announce it with the `Command` event
|
|
196
|
+
- `onlyCommand(myCommandId, c.target)` ensures the trusted caller hit the right endpoint
|
|
197
|
+
|
|
198
|
+
## Step 6: Read Input With A Cursor
|
|
199
|
+
|
|
200
|
+
Cursor parsing is the nicest way to read structured command input.
|
|
201
|
+
|
|
202
|
+
If your request contains a bundled input like:
|
|
203
|
+
|
|
204
|
+
- `route(uint foo) & amount(bytes32 asset, bytes32 meta, uint amount)`
|
|
205
|
+
|
|
206
|
+
your command can:
|
|
207
|
+
|
|
208
|
+
- open it with `Cursors.openBlock(...)`
|
|
209
|
+
- consume the route first
|
|
210
|
+
- then consume the amount
|
|
211
|
+
- keep parsing in bundle/member order without indexing helpers
|
|
212
|
+
|
|
213
|
+
For simple projects, it is perfectly fine to:
|
|
214
|
+
|
|
215
|
+
- publish the full input schema string in the `Command` event
|
|
216
|
+
- encode bundled input blocks off-chain
|
|
217
|
+
- decode them sequentially with cursor helpers inside the command
|
|
218
|
+
|
|
219
|
+
## Step 7: Return State With Writers
|
|
220
|
+
|
|
221
|
+
When your command needs to build response blocks manually, use `Writers`.
|
|
222
|
+
|
|
223
|
+
```solidity
|
|
224
|
+
// SPDX-License-Identifier: MIT
|
|
225
|
+
pragma solidity ^0.8.33;
|
|
226
|
+
|
|
227
|
+
import {Writers, Writer} from "@rootzero/contracts/Cursors.sol";
|
|
228
|
+
|
|
229
|
+
using Writers for Writer;
|
|
230
|
+
|
|
231
|
+
function buildBalances() internal pure returns (bytes memory) {
|
|
232
|
+
Writer memory writer = Writers.allocBalances(2);
|
|
233
|
+
writer.appendBalance(bytes32(uint256(1)), bytes32(0), 50);
|
|
234
|
+
writer.appendBalance(bytes32(uint256(2)), bytes32(0), 75);
|
|
235
|
+
return writer.done();
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Use this when your command needs to return:
|
|
240
|
+
|
|
241
|
+
- balances
|
|
242
|
+
- custodies
|
|
243
|
+
- transactions
|
|
244
|
+
|
|
245
|
+
If you are only consuming built-in commands, you often will not need to touch writers directly.
|
|
246
|
+
|
|
247
|
+
## A Tiny End-To-End Example
|
|
248
|
+
|
|
249
|
+
Imagine you want a host that keeps internal balances and lets rootzero debit them.
|
|
250
|
+
|
|
251
|
+
1. Deploy a host that inherits `Host` and `DebitAccount`.
|
|
252
|
+
2. Store balances in your own mapping.
|
|
253
|
+
3. Implement `debitAccount(account, asset, meta, amount)`.
|
|
254
|
+
4. Send `debitAccountToBalance` a request containing one or more `AMOUNT` blocks.
|
|
255
|
+
5. rootzero returns `BALANCE` blocks representing the debited amounts.
|
|
256
|
+
|
|
257
|
+
That is already a valid and useful integration.
|
|
258
|
+
|
|
259
|
+
## Which Files To Open Next
|
|
260
|
+
|
|
261
|
+
If you want to learn by example, these are the best files to read next:
|
|
262
|
+
|
|
263
|
+
- `examples/1-Host.sol`: smallest host
|
|
264
|
+
- `examples/2-Basic.sol`: host plus a built-in command hook
|
|
265
|
+
- `examples/3-Command.sol`: custom command id and command event
|
|
266
|
+
- `examples/4-Batch.sol`: batching request input and building balance output
|
|
267
|
+
- `examples/5-Route.sol`: bundled route input plus protocol blocks
|
|
268
|
+
- `test/commands.test.ts`: concrete request and response examples
|
|
269
|
+
- `test/helpers/blocks.ts`: block encoders you can reuse in off-chain tooling
|
|
270
|
+
|
|
271
|
+
## Common Mistakes
|
|
272
|
+
|
|
273
|
+
- Passing data in `state` when the command expects it in `request`
|
|
274
|
+
- Forgetting to emit a `Command` event for a custom command
|
|
275
|
+
- Using the wrong `target` value for `onlyCommand`
|
|
276
|
+
- Trying to parse raw bytes manually when a built-in reader already exists
|
|
277
|
+
- Starting with a custom command when a built-in module already matches the job
|
|
278
|
+
|
|
279
|
+
## Recommended Learning Order
|
|
280
|
+
|
|
281
|
+
1. Deploy a plain `Host`.
|
|
282
|
+
2. Add one built-in command such as `DebitAccount` or `Deposit`.
|
|
283
|
+
3. Use the TypeScript block helpers to build requests.
|
|
284
|
+
4. Only then add a custom command with bundled input and cursor parsing.
|
|
285
|
+
|
|
286
|
+
That path keeps the first integration small and easy to debug.
|
package/package.json
CHANGED
|
@@ -1,33 +1,17 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@rootzero/contracts",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "Solidity contracts and protocol building blocks for rootzero hosts and commands.",
|
|
5
|
-
"private": false,
|
|
6
|
-
"license": "GPL-3.0-only",
|
|
7
|
-
"type": "module",
|
|
8
|
-
"files": [
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
],
|
|
14
|
-
"publishConfig": {
|
|
15
|
-
"access": "public"
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
"compile": "hardhat compile",
|
|
19
|
-
"test": "hardhat test",
|
|
20
|
-
"prepublishOnly": "npm test",
|
|
21
|
-
"prepare:package": "node scripts/prepare-package.mjs",
|
|
22
|
-
"pack:package": "npm run prepare:package && npm pack ./dist/package",
|
|
23
|
-
"publish:package": "npm test && npm run prepare:package && npm publish ./dist/package --access public"
|
|
24
|
-
},
|
|
25
|
-
"devDependencies": {
|
|
26
|
-
"@nomicfoundation/hardhat-mocha": "^3.0.13",
|
|
27
|
-
"chai": "^6.2.2",
|
|
28
|
-
"ethers": "^6.16.0",
|
|
29
|
-
"hardhat": "^3.1.7",
|
|
30
|
-
"mocha": "^11.7.5",
|
|
31
|
-
"typescript": "~5.8.0"
|
|
32
|
-
}
|
|
33
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@rootzero/contracts",
|
|
3
|
+
"version": "0.5.1",
|
|
4
|
+
"description": "Solidity contracts and protocol building blocks for rootzero hosts and commands.",
|
|
5
|
+
"private": false,
|
|
6
|
+
"license": "GPL-3.0-only",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"files": [
|
|
9
|
+
"**/*.sol",
|
|
10
|
+
"README.md",
|
|
11
|
+
"LICENSE",
|
|
12
|
+
"docs/GETTING_STARTED.md"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
pragma solidity ^0.8.33;
|
|
3
|
-
|
|
4
|
-
import { Host } from "../core/Host.sol";
|
|
5
|
-
import { BorrowAgainstCustodyToBalance } from "../commands/Borrow.sol";
|
|
6
|
-
import { AssetAmount, HostAmount } from "../blocks/Schema.sol";
|
|
7
|
-
import { Cur, Cursors, Keys } from "../Cursors.sol";
|
|
8
|
-
import { Ids } from "../utils/Ids.sol";
|
|
9
|
-
|
|
10
|
-
using Cursors for Cur;
|
|
11
|
-
|
|
12
|
-
contract TestBorrowHost is Host, BorrowAgainstCustodyToBalance {
|
|
13
|
-
event BorrowCalled(bytes32 account, bytes32 asset, bytes32 meta, uint amount, bytes inputData);
|
|
14
|
-
|
|
15
|
-
bytes32 public returnAsset;
|
|
16
|
-
bytes32 public returnMeta;
|
|
17
|
-
uint public returnAmount;
|
|
18
|
-
|
|
19
|
-
constructor(address cmdr) Host(address(0), 1, "test") BorrowAgainstCustodyToBalance("") {
|
|
20
|
-
if (cmdr != address(0)) access(Ids.toHost(cmdr), true);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function setReturn(bytes32 asset, bytes32 meta, uint amount) external {
|
|
24
|
-
returnAsset = asset;
|
|
25
|
-
returnMeta = meta;
|
|
26
|
-
returnAmount = amount;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function borrowAgainstCustodyToBalance(
|
|
30
|
-
bytes32 account,
|
|
31
|
-
HostAmount memory custody,
|
|
32
|
-
Cur memory request
|
|
33
|
-
) internal override returns (AssetAmount memory) {
|
|
34
|
-
if (request.i < request.len) {
|
|
35
|
-
bytes calldata inputData;
|
|
36
|
-
(bytes4 key, uint len) = request.peek(request.i);
|
|
37
|
-
if (key == Keys.Bundle) {
|
|
38
|
-
Cur memory bundle = request.bundle();
|
|
39
|
-
(key, len) = bundle.peek(bundle.i);
|
|
40
|
-
if (key == Keys.Route) {
|
|
41
|
-
inputData = bundle.unpackRoute();
|
|
42
|
-
} else {
|
|
43
|
-
uint next = bundle.i + 8 + len;
|
|
44
|
-
inputData = msg.data[bundle.offset + bundle.i:bundle.offset + next];
|
|
45
|
-
bundle.i = next;
|
|
46
|
-
}
|
|
47
|
-
} else if (key == Keys.Route) {
|
|
48
|
-
inputData = request.unpackRoute();
|
|
49
|
-
} else {
|
|
50
|
-
uint next = request.i + 8 + len;
|
|
51
|
-
inputData = msg.data[request.offset + request.i:request.offset + next];
|
|
52
|
-
request.i = next;
|
|
53
|
-
}
|
|
54
|
-
emit BorrowCalled(account, custody.asset, custody.meta, custody.amount, inputData);
|
|
55
|
-
} else {
|
|
56
|
-
emit BorrowCalled(account, custody.asset, custody.meta, custody.amount, "");
|
|
57
|
-
}
|
|
58
|
-
return AssetAmount({asset: returnAsset, meta: returnMeta, amount: returnAmount});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function getBorrowId() external view returns (uint) {
|
|
62
|
-
return borrowAgainstCustodyToBalanceId;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function getAdminAccount() external view returns (bytes32) {
|
|
66
|
-
return adminAccount;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
pragma solidity ^0.8.33;
|
|
3
|
-
|
|
4
|
-
import { Host } from "../core/Host.sol";
|
|
5
|
-
import { Burn } from "../commands/Burn.sol";
|
|
6
|
-
import { Ids } from "../utils/Ids.sol";
|
|
7
|
-
|
|
8
|
-
contract TestBurnHost is Host, Burn {
|
|
9
|
-
event BurnCalled(bytes32 account, bytes32 asset, bytes32 meta, uint amount);
|
|
10
|
-
|
|
11
|
-
constructor(address cmdr)
|
|
12
|
-
Host(address(0), 1, "test")
|
|
13
|
-
Burn()
|
|
14
|
-
{
|
|
15
|
-
if (cmdr != address(0)) access(Ids.toHost(cmdr), true);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function burn(bytes32 account, bytes32 asset, bytes32 meta, uint amount)
|
|
19
|
-
internal override
|
|
20
|
-
returns (uint)
|
|
21
|
-
{
|
|
22
|
-
emit BurnCalled(account, asset, meta, amount);
|
|
23
|
-
return amount;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function getBurnId() external view returns (uint) { return burnId; }
|
|
27
|
-
function getAdminAccount() external view returns (bytes32) { return adminAccount; }
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
pragma solidity ^0.8.33;
|
|
3
|
-
|
|
4
|
-
import { Host } from "../core/Host.sol";
|
|
5
|
-
import { Create } from "../commands/Create.sol";
|
|
6
|
-
import { Cursors, Cur, Keys } from "../Cursors.sol";
|
|
7
|
-
import { Ids } from "../utils/Ids.sol";
|
|
8
|
-
|
|
9
|
-
using Cursors for Cur;
|
|
10
|
-
|
|
11
|
-
contract TestCreateHost is Host, Create {
|
|
12
|
-
event CreateCalled(bytes32 account, bytes inputData);
|
|
13
|
-
|
|
14
|
-
constructor(address cmdr)
|
|
15
|
-
Host(address(0), 1, "test")
|
|
16
|
-
Create("")
|
|
17
|
-
{
|
|
18
|
-
if (cmdr != address(0)) access(Ids.toHost(cmdr), true);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function create(bytes32 account, Cur memory input) internal override {
|
|
22
|
-
(bytes4 key, uint len) = input.peek(input.i);
|
|
23
|
-
bytes calldata inputData;
|
|
24
|
-
if (key == Keys.Route) {
|
|
25
|
-
inputData = input.unpackRoute();
|
|
26
|
-
} else {
|
|
27
|
-
uint next = input.i + 8 + len;
|
|
28
|
-
inputData = msg.data[input.offset + input.i:input.offset + next];
|
|
29
|
-
input.i = next;
|
|
30
|
-
}
|
|
31
|
-
emit CreateCalled(account, inputData);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function getCreateId() external view returns (uint) { return createId; }
|
|
35
|
-
function getAdminAccount() external view returns (bytes32) { return adminAccount; }
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
pragma solidity ^0.8.33;
|
|
3
|
-
|
|
4
|
-
import { Tx, Sizes } from "../blocks/Schema.sol";
|
|
5
|
-
import { Cur, Cursors, Writer } from "../Cursors.sol";
|
|
6
|
-
import { Writers } from "../blocks/Writers.sol";
|
|
7
|
-
|
|
8
|
-
using Cursors for Cur;
|
|
9
|
-
using Writers for Writer;
|
|
10
|
-
|
|
11
|
-
contract TestCursorHelper {
|
|
12
|
-
function testBlockHeader(bytes4 key, uint len) external pure returns (uint) {
|
|
13
|
-
return Writers.toBlockHeader(key, len);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function testWriteBalanceBlock(bytes32 asset, bytes32 meta, uint amount) external pure returns (bytes memory) {
|
|
17
|
-
Writer memory w = Writers.alloc(Sizes.Balance);
|
|
18
|
-
w.appendBalance(asset, meta, amount);
|
|
19
|
-
return w.dst;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function testWriteCustodyBlock(
|
|
23
|
-
uint host_,
|
|
24
|
-
bytes32 asset,
|
|
25
|
-
bytes32 meta,
|
|
26
|
-
uint amount
|
|
27
|
-
) external pure returns (bytes memory) {
|
|
28
|
-
Writer memory w = Writers.alloc(Sizes.Custody);
|
|
29
|
-
w.appendCustody(host_, asset, meta, amount);
|
|
30
|
-
return w.dst;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function testWriteTxBlock(
|
|
34
|
-
bytes32 from_,
|
|
35
|
-
bytes32 to_,
|
|
36
|
-
bytes32 asset,
|
|
37
|
-
bytes32 meta,
|
|
38
|
-
uint amount
|
|
39
|
-
) external pure returns (bytes memory) {
|
|
40
|
-
Writer memory w = Writers.alloc(Sizes.Transaction);
|
|
41
|
-
w.appendTx(Tx({ from: from_, to: to_, asset: asset, meta: meta, amount: amount }));
|
|
42
|
-
return w.dst;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function testToBountyBlock(uint amount, bytes32 relayer) external pure returns (bytes memory) {
|
|
46
|
-
return Cursors.toBountyBlock(amount, relayer);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function testToBalanceBlock(bytes32 asset, bytes32 meta, uint amount) external pure returns (bytes memory) {
|
|
50
|
-
return Cursors.toBalanceBlock(asset, meta, amount);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function testToCustodyBlock(
|
|
54
|
-
uint host_,
|
|
55
|
-
bytes32 asset,
|
|
56
|
-
bytes32 meta,
|
|
57
|
-
uint amount
|
|
58
|
-
) external pure returns (bytes memory) {
|
|
59
|
-
return Cursors.toCustodyBlock(host_, asset, meta, amount);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function testWriterFinishIncomplete() external pure returns (bytes memory) {
|
|
63
|
-
Writer memory w = Writers.alloc(Sizes.Balance);
|
|
64
|
-
return Writers.finish(w);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function testWriterFinish(bytes32 asset, bytes32 meta, uint amount) external pure returns (bytes memory) {
|
|
68
|
-
Writer memory w = Writers.alloc(Sizes.Balance * 2);
|
|
69
|
-
w.appendBalance(asset, meta, amount);
|
|
70
|
-
return Writers.finish(w);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function testUnpackBalance(bytes calldata source) external pure returns (bytes32 asset, bytes32 meta, uint amount) {
|
|
74
|
-
Cur memory cur = Cursors.open(source);
|
|
75
|
-
return cur.unpackBalance();
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function testToTxValue(bytes calldata source) external pure returns (bytes32 from_, bytes32 to_, bytes32 asset, bytes32 meta, uint amount) {
|
|
79
|
-
Cur memory cur = Cursors.open(source);
|
|
80
|
-
Tx memory value = cur.unpackTxValue();
|
|
81
|
-
return (value.from, value.to, value.asset, value.meta, value.amount);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function testPrimeRun(bytes calldata source, uint group)
|
|
85
|
-
external
|
|
86
|
-
pure
|
|
87
|
-
returns (bytes4 key, uint count, uint quotient, uint offset, uint i, uint len, uint bound)
|
|
88
|
-
{
|
|
89
|
-
uint sourceOffset;
|
|
90
|
-
assembly ("memory-safe") {
|
|
91
|
-
sourceOffset := source.offset
|
|
92
|
-
}
|
|
93
|
-
Cur memory cur = Cursors.open(source);
|
|
94
|
-
(key, count, quotient) = cur.primeRun(group);
|
|
95
|
-
return (key, count, quotient, cur.offset - sourceOffset, cur.i, cur.len, cur.bound);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function testPeek(bytes calldata source, uint i) external pure returns (bytes4 key, uint len) {
|
|
99
|
-
Cur memory cur = Cursors.open(source);
|
|
100
|
-
return cur.peek(i);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function testCountRun(bytes calldata source, uint i, bytes4 key) external pure returns (uint total, uint next) {
|
|
104
|
-
Cur memory cur = Cursors.open(source);
|
|
105
|
-
return cur.countRun(i, key);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function testBundle(bytes calldata source) external pure returns (uint inputI, uint offset, uint len) {
|
|
109
|
-
uint sourceOffset;
|
|
110
|
-
assembly ("memory-safe") {
|
|
111
|
-
sourceOffset := source.offset
|
|
112
|
-
}
|
|
113
|
-
Cur memory cur = Cursors.open(source);
|
|
114
|
-
Cur memory out = cur.bundle();
|
|
115
|
-
return (cur.i, out.offset - sourceOffset, out.len);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function testUnpackStep(bytes calldata source) external pure returns (uint target, uint value, bytes calldata req, uint i) {
|
|
119
|
-
Cur memory cur = Cursors.open(source);
|
|
120
|
-
(target, value, req) = cur.unpackStep();
|
|
121
|
-
return (target, value, req, cur.i);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function testRequireAmount(
|
|
125
|
-
bytes calldata source,
|
|
126
|
-
bytes32 asset,
|
|
127
|
-
bytes32 meta
|
|
128
|
-
) external pure returns (uint amount, uint i) {
|
|
129
|
-
Cur memory cur = Cursors.open(source);
|
|
130
|
-
amount = cur.requireAmount(asset, meta);
|
|
131
|
-
i = cur.i;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function testRequireAuth(bytes calldata source, uint cid) external pure returns (uint deadline, bytes calldata proof, uint i) {
|
|
135
|
-
Cur memory cur = Cursors.open(source);
|
|
136
|
-
(deadline, proof) = cur.requireAuth(cid);
|
|
137
|
-
return (deadline, proof, cur.i);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function testNodeAfter(bytes calldata source, uint group, uint backup) external pure returns (uint) {
|
|
141
|
-
Cur memory cur = Cursors.open(source);
|
|
142
|
-
cur.primeRun(group);
|
|
143
|
-
return cur.nodeAfter(backup);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function testRecipientAfter(bytes calldata source, uint group, bytes32 backup) external pure returns (bytes32) {
|
|
147
|
-
Cur memory cur = Cursors.open(source);
|
|
148
|
-
cur.primeRun(group);
|
|
149
|
-
return cur.recipientAfter(backup);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function testAuthLast(
|
|
153
|
-
bytes calldata source,
|
|
154
|
-
uint group,
|
|
155
|
-
uint cid
|
|
156
|
-
) external pure returns (bytes32 hash, uint deadline, bytes calldata proof) {
|
|
157
|
-
Cur memory cur = Cursors.open(source);
|
|
158
|
-
cur.primeRun(group);
|
|
159
|
-
return cur.authLast(cid);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function testCursorCompleteEmpty(bytes calldata source, uint group) external pure returns (bool) {
|
|
163
|
-
Cur memory cur = Cursors.open(source);
|
|
164
|
-
cur.primeRun(group);
|
|
165
|
-
cur.complete();
|
|
166
|
-
return true;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function testCursorCompletePartial(bytes calldata source, uint group) external pure returns (bool) {
|
|
170
|
-
Cur memory cur = Cursors.open(source);
|
|
171
|
-
cur.primeRun(group);
|
|
172
|
-
if (cur.bound > 0) {
|
|
173
|
-
(, uint len) = cur.peek(cur.i);
|
|
174
|
-
cur.i += 8 + len;
|
|
175
|
-
}
|
|
176
|
-
cur.complete();
|
|
177
|
-
return true;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function testCursorCompleteConsumed(bytes calldata source, uint group) external pure returns (bool) {
|
|
181
|
-
Cur memory cur = Cursors.open(source);
|
|
182
|
-
cur.primeRun(group);
|
|
183
|
-
while (cur.i < cur.bound) {
|
|
184
|
-
(, uint len) = cur.peek(cur.i);
|
|
185
|
-
cur.i += 8 + len;
|
|
186
|
-
}
|
|
187
|
-
cur.complete();
|
|
188
|
-
return true;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
}
|