Снятие делегации
POST /v1/revocations снимает делегат с депозитного адреса: адрес снова становится обычным EOA, без кода. Газ платит
ваш газ-кошелёк, комиссии нет.
Когда это нужно
Заголовок раздела «Когда это нужно»- На адрес присылают нативную монету с бирж с фиксированным лимитом газа 21 000. Такие переводы на адрес с кодом падают: см. Депозиты с бирж.
- Вы перестаёте пользоваться Portuna Sweep и хотите вернуть адреса в исходный вид.
Чтобы сметать адрес в другой горячий кошелёк (котёл), снимать делегацию не нужно: подпишите авторизацию на делегат того кошелька и пришлите её со следующим sweep.
Как это устроено
Заголовок раздела «Как это устроено»Депозитный адрес подписывает авторизацию на нулевой адрес 0x0000000000000000000000000000000000000000 со своим
текущим nonce. Газ-кошелёк отправляет её транзакцией самому себе; несколько снятий уходят одной транзакцией.
- Nonce. Каждая применённая сетью авторизация увеличивает nonce адреса на 1, поэтому после первого sweep он обычно равен 1. Если первый sweep адреса ещё в очереди, nonce снятия должен быть на единицу больше.
- Порядок. Снятие ждёт, пока уйдут sweep этого же адреса, запрошенные раньше него.
- Потом. Чтобы снова сметать адрес, нужна новая авторизация на делегат — как для первого sweep.
Подписать и отправить
Заголовок раздела «Подписать и отправить»Подпишите авторизацию так же, как для sweep (см. Подпись авторизаций), но с нулевым адресом и текущим nonce, затем:
curl -X POST "https://api-testnet.portuna.io/v1/revocations" \ -H "Authorization: Bearer $PORTUNA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "external_id": "revoke-2c75", "chain_id": 11155111, "account": "0x2c7536E3605D9C16a7a3D7b1898e529396a65c23", "authorization": { "chain_id": 11155111, "address": "0x0000000000000000000000000000000000000000", "nonce": 1, "y_parity": 1, "r": "0x04bec751666b1f63d7f4572d81a38642d47517b63b84f6b94a0a494a198c315d", "s": "0x2a3cc8c27acdae37a0eb652d394ff698ad3910adbfa4d7a393c49a6d4099cabb" }}'Ответ — 202 со снятием в статусе queued; следите за ним через GET /v1/revocations/{id}.
// The nonce the address will have when the revocation goes out: its current one (1 after the first sweep),// plus 1 if its first sweep is still queued.nonce, err := eth.NonceAt(ctx, deposit, nil)if err != nil { log.Fatal(err)}digest, err := portuna.RevocationDigest(sepolia, nonce)if err != nil { log.Fatal(err)}sig, err := crypto.Sign(digest[:], key) // your HSM, KMS or MPC in productionif err != nil { log.Fatal(err)}auth, err := portuna.RevocationFromSignature(sepolia, nonce, deposit, sig)if err != nil { log.Fatal(err)}revocation, _, err := client.CreateRevocation(ctx, portuna.RevocationRequest{ ExternalID: "revoke-" + deposit.Hex(), ChainID: sepolia, Account: deposit, Authorization: auth,})if err != nil { log.Fatal(err)}for revocation.Status == portuna.RevocationQueued || revocation.Status == portuna.RevocationProcessing { time.Sleep(5 * time.Second) if revocation, err = client.Revocation(ctx, revocation.ID); err != nil { log.Fatal(err) }}fmt.Println(revocation.Status, revocation.Reason, revocation.TxHash)// The nonce the address will have when the revocation goes out: its current one (1 after the first sweep),// plus 1 if its first sweep is still queued.const nonce = await rpc.getTransactionCount({ address: deposit })const digest = revocationDigest({ chainId, nonce })const signature = await sign({ hash: digest, privateKey, to: 'hex' }) // your HSM, KMS or MPC in productionconst authorization = await revocationFromSignature({ chainId, nonce, deposit, signature })
let { revocation } = await client.createRevocation({ external_id: `revoke-${deposit}`, chain_id: chainId, account: deposit, authorization,})while (revocation.status === 'queued' || revocation.status === 'processing') { await new Promise((resolve) => setTimeout(resolve, 5000)) revocation = await client.revocation(revocation.id)}console.log(revocation.status, revocation.reason, revocation.tx_hash)В своей кастоди подписывайте RevocationDigest (revocationDigest) так же, как хэш авторизации, а «сырую» подпись
переводите в формат API функцией RevocationFromSignature (revocationFromSignature).
| Статус | reason | Что делать |
|---|---|---|
revoked | Готово: кода на адресе нет. Приходит событие revocation.revoked. | |
rejected | not_delegated | Ничего: делегации на адресе не было. |
rejected | stale_authorization | Nonce изменился. Подпишите заново с текущим nonce. |
rejected | invalid_authorization | Авторизация не прошла проверку перед отправкой. Подпишите заново. |
failed | not_applied | Транзакция прошла, но сеть пропустила авторизацию: nonce сменился после проверки. Подпишите заново. |
failed | другой код | Попытки кончились: см. причины неудачной попытки — например, insufficient_gas_balance. |
Стоимость
Заголовок раздела «Стоимость»Только газ — с вашего газ-кошелька, без комиссии. Одно снятие занимает около 28 800 газа в Sepolia по правилам
Glamsterdam и 36 800 по прежним правилам мейннета Ethereum и BSC; в транзакции с десятками снятий — около 20 000 на
адрес по прежним правилам. Поля gas и gas_cost снятия показывают его долю.