1Do App
Will
The wallet signs beneficiaries, weights, trigger mode, version, and executor fee; first activation locks the plan and later calls can settle separate assets.
Plan structure and signature
WillPlan is signed with the EIP-712 domain Will Plan on 1Do / 1, the wallet as verifyingContract, and wallet ERC-1271 validation. Beneficiary order is part of the hash.
The tokens array is not signed. An executor supplies it on each executeWill call, so the plan fixes distribution rules but not the asset list, signing-time balances, or future settlement amounts.
- beneficiaries contain nonzero addresses and relative weights. Zero-weight entries are allowed, but total weight must be greater than zero.
- triggerMode 0 is fixed time and mode 1 is inactivity; inactivity mode may additionally require timeUnlock.
- expiresAt is the deadline for first activation. Activation remains valid exactly at the timestamp, but expiresAt = 0 is immediately expired rather than unlimited.
- willVersion starts at 1 and increments on resetWill, invalidating signatures for older versions.
- executorFeeBps is capped at 100 bps, or 1% of each settled asset balance.
struct Beneficiary {
address addr;
uint256 weight;
}
struct WillPlan {
uint256 expiresAt;
uint256 timeUnlock;
uint256 executorFeeBps;
uint8 triggerMode;
uint256 willVersion;
Beneficiary[] beneficiaries;
}Trigger configuration
configureInactivity(delay, grace) sets inactiveDelay and gracePeriod and immediately records the current time as lastAlive. ping() later refreshes lastAlive; inactivity becomes true at lastAlive + inactiveDelay + gracePeriod.
Inactivity configuration is not included in the signed plan and the wallet may change it after signing. configureInactivity, ping, setPaused, and resetWill all require wallet self-calls.
- Fixed-time mode requires block.timestamp >= timeUnlock.
- Inactivity mode requires configured lastAlive, paused = false, and the delay plus grace deadline. A nonzero timeUnlock must also be reached.
- setPaused(true) only makes inactivity false. It does not pause fixed-time plans or continuation of an already active plan.
- Pausing and unpausing do not refresh lastAlive. If the deadline passes while paused, unpausing may make inactivity immediately true.
First activation
Submit plan and signature
Any executor may call executeWill, but version, fee, expiry, trigger conditions, and the wallet signature must all be valid.
Select executable assets
The caller supplies native or ERC-20 addresses; at least one must have nonzero balance and remain unsettled in the current version.
Lock the plan
The first successful execution stores activePlanHash. It is the WillPlan struct hash, not the full domain-separated EIP-712 digest.
Settle the first batch
The same transaction distributes executable assets and emits WillActivated and TokenSettled. Any transfer failure reverts the entire batch.
One active plan per version
Several valid same-version signatures may exist offchain, but the first plan successfully executed locks that version. To replace it, the wallet should resetWill before signing the next version.
Incremental asset settlement
- After activation, later calls must resubmit the identical plan. selfSig, expiresAt, and trigger conditions are no longer checked, so selfSig may be empty.
- address(0) represents native currency; other addresses represent ERC-20. NFTs are not currently supported.
- Each asset settles at most once per willVersion. Duplicate addresses are skipped, while a zero-balance asset remains unsettled and may be processed after receiving funds.
- If every supplied asset has zero balance or is already settled, the call reverts with NoExecutableToken. A multi-asset batch is atomic.
- Funds arriving after an asset has settled cannot be distributed again under the same version; resetWill and a newly signed plan are required.
- expiresAt limits first activation only. Once active, later assets may settle after that timestamp.
Weights, rounding, and executor fees
Each asset uses the wallet's entire live balance at execution. The current caller receives the executor fee first, then the remainder is distributed by weight. Different batches may reward different executors.
- All integer-division dust goes to the final positive-weight beneficiary so the distributable balance is fully allocated.
- Signing does not freeze, reserve, or move assets; balances may change before execution.
executor fee = floor(balance × executorFeeBps / 10,000)
remaining = balance - executor fee
share = floor(remaining × weight / totalWeight)State reads and events
- inactivityState() returns lastPing, inactiveDelay, gracePeriod, paused, and the current inactive result.
- willState() returns currentWillVersion and activePlanHash; isTokenSettled(token) refers only to the current version.
- The chain does not store the full plan, beneficiary list, or signature. It stores the plan hash, version, heartbeat configuration, and per-asset settlement version, so clients must retain the original plan safely.
- InactivityConfigUpdated, PingRecorded, PausedSet, WillReset, WillActivated, and TokenSettled are emitted by the wallet address.
Lifecycle and boundaries
- resetWill changes version instead of clearing every settled mapping entry. It clears activePlanHash but leaves heartbeat configuration and paused state unchanged.
- App disablement, Registry blocking, or Runtime version changes may affect future execution; Will is not an independently custodied vault.
- Execution can distribute only transferable balances that actually exist at settlement time and does not guarantee present or future funds.
Onchain asset distribution, not a legal guarantee
Will implements conditional onchain asset execution. It does not validate legal-will requirements, identity, tax treatment, or jurisdiction-specific enforceability.