Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ibc: transparent address support #1950

Merged
merged 20 commits into from
Dec 22, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/perfect-colts-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@penumbra-zone/protobuf': major
'@penumbra-zone/services': minor
'@penumbra-zone/bech32m': minor
'minifront': minor
'@penumbra-zone/types': minor
'@penumbra-zone/wasm': minor
---

support transparent addresses for usdc noble IBC withdrawals
30 changes: 20 additions & 10 deletions apps/minifront/src/state/ibc-in/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import { getAddrByIndex } from '../../fetchers/address';
import { bech32mAddress } from '@penumbra-zone/bech32m/penumbra';
import { Toast } from '@penumbra-zone/ui-deprecated/lib/toast/toast';
import { shorten } from '@penumbra-zone/types/string';
import { Address } from '@penumbra-zone/protobuf/penumbra/core/keys/v1/keys_pb';
import { bech32CompatAddress } from '@penumbra-zone/bech32m/penumbracompat1';
import { calculateFee, GasPrice, SigningStargateClient } from '@cosmjs/stargate';
import { chains } from 'chain-registry';
import { getChainId } from '../../fetchers/chain-id';
Expand All @@ -19,9 +17,10 @@ import { currentTimePlusTwoDaysRounded } from '../ibc-out';
import { EncodeObject } from '@cosmjs/proto-signing';
import { MsgTransfer } from 'cosmjs-types/ibc/applications/transfer/v1/tx';
import { parseRevisionNumberFromChainId } from './parse-revision-number-from-chain-id';
import { bech32ChainIds } from '../shared.ts';
import { penumbra } from '../../penumbra.ts';
import { TendermintProxyService } from '@penumbra-zone/protobuf';
import { TendermintProxyService, ViewService } from '@penumbra-zone/protobuf';
import { TransparentAddressRequest } from '@penumbra-zone/protobuf/penumbra/view/v1/view_pb';
import { bech32ChainIds } from '../shared.ts';

export interface IbcInSlice {
selectedChain?: ChainInfo;
Expand Down Expand Up @@ -135,10 +134,6 @@ const getExplorerPage = (txHash: string, chainId?: string) => {
return txPage.replace('${txHash}', txHash);
};

const getCompatibleBech32 = (chainId: string, address: Address): string => {
return bech32ChainIds.includes(chainId) ? bech32CompatAddress(address) : bech32mAddress(address);
};

export const getPenumbraAddress = async (
account: number,
chainId?: string,
Expand All @@ -147,7 +142,7 @@ export const getPenumbraAddress = async (
return undefined;
}
const receiverAddress = await getAddrByIndex(account, true);
return getCompatibleBech32(chainId, receiverAddress);
return bech32mAddress(receiverAddress);
};

const estimateFee = async ({
Expand Down Expand Up @@ -198,7 +193,7 @@ async function execute(
throw new Error('Penumbra chain id could not be retrieved');
}

const penumbraAddress = await getPenumbraAddress(account, selectedChain.chainId);
let penumbraAddress = await getPenumbraAddress(account, selectedChain.chainId);
if (!penumbraAddress) {
throw new Error('Penumbra address not available');
}
Expand All @@ -207,6 +202,21 @@ async function execute(
const assetMetadata = augmentToAsset(coin.raw.denom, selectedChain.chainName);

const transferToken = fromDisplayAmount(assetMetadata, coin.displayDenom, amount);

const { address: t_addr, encoding: encoding } = await penumbra
.service(ViewService)
.transparentAddress(new TransparentAddressRequest({}));
if (!t_addr) {
throw new Error('Error with generating IBC transparent address');
}

// Temporary: detect USDC Noble inbound transfers, and use a transparent (t-addr) encoding
// to ensure Bech32 encoding compatibility.
if (transferToken.denom.includes('uusdc') && bech32ChainIds.includes(selectedChain.chainId)) {
// Set the reciever address to the t-addr encoding.
penumbraAddress = encoding;
}

const params: MsgTransfer = {
sourcePort: 'transfer',
sourceChannel: await getCounterpartyChannelId(selectedChain, penumbraChainId),
Expand Down
29 changes: 25 additions & 4 deletions apps/minifront/src/state/ibc-out.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AllSlices, SliceCreator, useStore } from '.';
import {
BalancesResponse,
TransactionPlannerRequest,
TransparentAddressRequest,
} from '@penumbra-zone/protobuf/penumbra/view/v1/view_pb';
import { BigNumber } from 'bignumber.js';
import { ClientState } from '@penumbra-zone/protobuf/ibc/lightclients/tendermint/v1/tendermint_pb';
Expand All @@ -24,14 +25,14 @@ import { Channel } from '@penumbra-zone/protobuf/ibc/core/channel/v1/channel_pb'
import { BLOCKS_PER_HOUR } from './constants';
import { createZQuery, ZQueryState } from '@penumbra-zone/zquery';
import { getChains } from '../fetchers/registry';
import { bech32ChainIds } from './shared';
import { penumbra } from '../penumbra';
import {
IbcChannelService,
IbcClientService,
IbcConnectionService,
ViewService,
} from '@penumbra-zone/protobuf';
import { bech32ChainIds } from './shared';

export const { chains, useChains } = createZQuery({
name: 'chains',
Expand Down Expand Up @@ -206,7 +207,7 @@ const getPlanRequest = async ({
}

const addressIndex = getAddressIndex(selection.accountAddress);
const { address: returnAddress } = await penumbra
let { address: returnAddress } = await penumbra
.service(ViewService)
.ephemeralAddress({ addressIndex });
if (!returnAddress) {
Expand All @@ -215,20 +216,40 @@ const getPlanRequest = async ({

const { timeoutHeight, timeoutTime } = await getTimeout(chain.channelId);

// Request transparent address from view service
const { address: t_addr } = await penumbra
.service(ViewService)
.transparentAddress(new TransparentAddressRequest({}));
if (!t_addr) {
throw new Error('Error with generating IBC transparent address');
}

// IBC-related fields
const denom = getMetadata(selection.balanceView).base;
let useTransparentAddress = false;

// Temporary: detect USDC Noble withdrawals, and use a transparent (t-addr) return
// address to ensure Bech32 encoding compatibility.
if (denom.includes('uusdc') && bech32ChainIds.includes(chain.chainId)) {
// Outbound IBC transfers timeout without setting either of these fields.
useTransparentAddress = true;
returnAddress = t_addr;
}

return new TransactionPlannerRequest({
ics20Withdrawals: [
{
amount: toBaseUnit(
BigNumber(amount),
getDisplayDenomExponentFromValueView(selection.balanceView),
),
denom: { denom: getMetadata(selection.balanceView).base },
denom: { denom },
destinationChainAddress,
returnAddress,
timeoutHeight,
timeoutTime,
sourceChannel: chain.channelId,
useCompatAddress: bech32ChainIds.includes(chain.chainId),
useTransparentAddress,
TalDerei marked this conversation as resolved.
Show resolved Hide resolved
},
],
source: addressIndex,
Expand Down
1 change: 0 additions & 1 deletion packages/bech32m/src/format/bytes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export const ByteLength = {
passet: 32,
pauctid: 32,
penumbra: 80,
penumbracompat1: 80,
penumbrafullviewingkey: 64,
penumbragovern: 32,
penumbraspendkey: 32,
Expand Down
6 changes: 0 additions & 6 deletions packages/bech32m/src/format/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,6 @@ export default {
byteLength: ByteLength.penumbra,
innerName: Inner.penumbra,
},
penumbracompat1: {
TalDerei marked this conversation as resolved.
Show resolved Hide resolved
prefix: Prefixes.penumbracompat1,
stringLength: StringLength.penumbracompat1,
byteLength: ByteLength.penumbracompat1,
innerName: Inner.penumbracompat1,
},
penumbrafullviewingkey: {
prefix: Prefixes.penumbrafullviewingkey,
stringLength: StringLength.penumbrafullviewingkey,
Expand Down
1 change: 0 additions & 1 deletion packages/bech32m/src/format/inner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export const Inner = {
passet: 'inner',
pauctid: 'inner',
penumbra: 'inner',
penumbracompat1: 'inner',
penumbrafullviewingkey: 'inner',
penumbragovern: 'gk',
penumbraspendkey: 'inner',
Expand Down
1 change: 0 additions & 1 deletion packages/bech32m/src/format/prefix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ export const Prefixes = {
passet: 'passet',
pauctid: 'pauctid',
penumbra: 'penumbra',
penumbracompat1: 'penumbracompat1',
penumbrafullviewingkey: 'penumbrafullviewingkey',
penumbragovern: 'penumbragovern',
penumbraspendkey: 'penumbraspendkey',
Expand Down
1 change: 0 additions & 1 deletion packages/bech32m/src/format/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export const StringLength = {
passet: 65,
pauctid: 66,
penumbra: 143,
penumbracompat1: 150,
penumbrafullviewingkey: 132,
penumbragovern: 73,
penumbraspendkey: 75,
Expand Down
24 changes: 0 additions & 24 deletions packages/bech32m/src/penumbracompat1.ts

This file was deleted.

25 changes: 0 additions & 25 deletions packages/bech32m/src/test/penumbracompat1.test.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/protobuf/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"gen:ibc": "buf generate buf.build/cosmos/ibc:7ab44ae956a0488ea04e04511efa5f70",
"gen:ics23": "buf generate buf.build/cosmos/ics23:55085f7c710a45f58fa09947208eb70b",
"gen:noble": "buf generate buf.build/noble-assets/forwarding:5a8609a6772d417584a9c60cd8b80881",
"gen:penumbra": "buf generate buf.build/penumbra-zone/penumbra:37cef73133644d9dbdeae95b644db3ec",
"gen:penumbra": "buf generate buf.build/penumbra-zone/penumbra:0a56a4f32c244e7eb277e02f6e85afbd",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"lint:strict": "tsc --noEmit && eslint src --max-warnings 0",
Expand Down
2 changes: 2 additions & 0 deletions packages/services/src/view-service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { unclaimedSwaps } from './unclaimed-swaps.js';
import { walletId } from './wallet-id.js';
import { witness } from './witness.js';
import { witnessAndBuild } from './witness-and-build.js';
import { transparentAddress } from './transparent-address.js';

export type Impl = ServiceImpl<typeof ViewService>;

Expand Down Expand Up @@ -62,4 +63,5 @@ export const viewImpl: Impl = {
walletId,
witness,
witnessAndBuild,
transparentAddress,
};
13 changes: 13 additions & 0 deletions packages/services/src/view-service/transparent-address.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Impl } from './index.js';
import { fvkCtx } from '../ctx/full-viewing-key.js';
import { getTransparentAddress } from '@penumbra-zone/wasm/keys';

export const transparentAddress: Impl['transparentAddress'] = async (_, ctx) => {
const fvk = await ctx.values.get(fvkCtx)();
const t_addr = getTransparentAddress(fvk);

return {
address: t_addr.address,
encoding: t_addr.encoding,
};
};
8 changes: 0 additions & 8 deletions packages/types/src/address.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,6 @@ import { describe, expect, test } from 'vitest';
import { parseIntoAddr } from './address.js';

describe('parseIntoAddr', () => {
test('works with compat', () => {
expect(() =>
parseIntoAddr(
'penumbracompat1147mfall0zr6am5r45qkwht7xqqrdsp50czde7empv7yq2nk3z8yyfh9k9520ddgswkmzar22vhz9dwtuem7uxw0qytfpv7lk3q9dp8ccaw2fn5c838rfackazmgf3ahhwqq0da',
),
).not.toThrow();
});

test('works with normal addresses', () => {
expect(() =>
parseIntoAddr(
Expand Down
4 changes: 0 additions & 4 deletions packages/types/src/address.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { Address } from '@penumbra-zone/protobuf/penumbra/core/keys/v1/keys_pb';
import { addressFromBech32m } from '@penumbra-zone/bech32m/penumbra';
import { compatAddressFromBech32, isCompatAddress } from '@penumbra-zone/bech32m/penumbracompat1';

export const parseIntoAddr = (addrStr: string): Address => {
if (isCompatAddress(addrStr)) {
return new Address(compatAddressFromBech32(addrStr));
}
return new Address(addressFromBech32m(addrStr));
};
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ export const Ics20WithdrawalComponent = ({ value }: { value: Ics20Withdrawal })
</ActionDetails.Row>
)}

<ActionDetails.Row label='Use Compat Address'>
{value.useCompatAddress ? 'TRUE' : 'FALSE'}
<ActionDetails.Row label='Use Transparent Address'>
{value.useTransparentAddress ? 'TRUE' : 'FALSE'}
</ActionDetails.Row>

{value.timeoutHeight && (
Expand Down
Loading