Skip to content

Commit ff390fa

Browse files
authored
Merge pull request #5223 from EdgeApp/sam/cardano-staking
Sam/cardano-staking
2 parents f9a01a3 + c5e1cf4 commit ff390fa

File tree

17 files changed

+659
-103
lines changed

17 files changed

+659
-103
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Unreleased
44

5+
- added: Cardano staking through Kiln staking pools
6+
- added: Support for `isLiquidStaking` field on staking policies
57
- changed: Some unecessary `showError` dropdowns demoted to hidden `showDevError`
68
- changed: Restrict Bity buy/sell to no-KYC asset
79
- changed: Determine Moonpay asset support using chainCode/contractAddress

src/components/scenes/Staking/StakeModifyScene.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { getPolicyIconUris, getPositionAllocations } from '../../../util/stakeUt
2626
import { toBigNumberString } from '../../../util/toBigNumberString'
2727
import { zeroString } from '../../../util/utils'
2828
import { EdgeCard } from '../../cards/EdgeCard'
29+
import { WarningCard } from '../../cards/WarningCard'
2930
import { SceneWrapper } from '../../common/SceneWrapper'
3031
import { withWallet } from '../../hoc/withWallet'
3132
import { ButtonsModal } from '../../modals/ButtonsModal'
@@ -110,6 +111,12 @@ const StakeModifySceneComponent = (props: Props) => {
110111
currencyCode: stakePolicy.rewardAssets[0].currencyCode,
111112
nativeAmount: existingAllocations.earned[0].nativeAmount
112113
})
114+
} else if ((modification === 'stake' || modification === 'unstake') && stakePolicy.isLiquidStaking === true) {
115+
setChangeQuoteRequest({
116+
...changeQuoteRequest,
117+
currencyCode: stakePolicy.rewardAssets[0].currencyCode,
118+
nativeAmount: '1' // Amounts for liquid staking are a noop
119+
})
113120
} else if (modification === 'unstake' && mustMaxUnstake) {
114121
setChangeQuoteRequest({
115122
...changeQuoteRequest,
@@ -455,7 +462,8 @@ const StakeModifySceneComponent = (props: Props) => {
455462
<EdgeCard icon={getCurrencyIconUris(wallet.currencyInfo.pluginId, null).symbolImage}>
456463
<EdgeRow title={lstrings.wc_smartcontract_wallet} body={getWalletName(wallet)} />
457464
</EdgeCard>
458-
{stakeUnstakeAllocations.map(renderEditableQuoteAmountRow)}
465+
{/* Editable rows are not show for liquid-staking policies */}
466+
{stakePolicy.isLiquidStaking !== true ? stakeUnstakeAllocations.map(renderEditableQuoteAmountRow) : null}
459467
{claimAllocations.map(renderEditableQuoteAmountRow)}
460468
{
461469
// Render stake/unstake fee tiles
@@ -476,6 +484,10 @@ const StakeModifySceneComponent = (props: Props) => {
476484
stakePolicy.stakeAssets.map(asset => renderFutureUnstakeFeeAmountRow(modification, asset))
477485
}
478486
{quoteInfo?.breakEvenDays != null ? renderBreakEvenDays() : null}
487+
{/* A warning card is show for liquid-staking policies, informing the user that staking is all-or-nothing. */}
488+
{stakePolicy.isLiquidStaking === true && (modification === 'stake' || modification === 'unstake') ? (
489+
<WarningCard title={lstrings.stake_liquid_staking_warning_title} header={sprintf(lstrings.stake_liquid_staking_warning_header, modification)} />
490+
) : null}
479491
{errorMessage === '' || sliderLocked ? null : <ErrorTile message={errorMessage} />}
480492
</View>
481493
)

src/components/themed/TransactionListTop.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,15 @@ export class TransactionListTopComponent extends React.PureComponent<Props, Stat
170170
let lockedNativeAmount = '0'
171171
const stakePositionMap: StakePositionMap = {}
172172
for (const stakePolicy of stakePolicies) {
173+
// Don't show liquid staking positions as locked amount
174+
if (stakePolicy.isLiquidStaking === true) continue
175+
173176
let total: string | undefined
174-
const { stakePolicyId } = stakePolicy
175177
const stakePlugin = getPluginFromPolicy(stakePlugins, stakePolicy)
176178
if (stakePlugin == null) continue
177179
try {
178180
const stakePosition = await stakePlugin.fetchStakePosition({
179-
stakePolicyId,
181+
stakePolicyId: stakePolicy.stakePolicyId,
180182
wallet: this.props.wallet,
181183
account: this.props.account
182184
})

src/constants/WalletAndCurrencyConstants.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,8 @@ export const SPECIAL_CURRENCY_INFO: {
288288
noChangeMiningFee: true,
289289
noMaxSpend: true,
290290
dummyPublicAddress: 'addr1qyh498v7479sljadw8mdlmshnlt3n30ewzpqnmvrsz2v8rpqt56tgy6jhzgcc7v8mlh7lhw9a9j2hdlmek4arx2238us9e5fq0',
291-
isImportKeySupported: true
291+
isImportKeySupported: true,
292+
isStakingSupported: true
292293
},
293294
cardanotestnet: {
294295
initWalletName: lstrings.string_first_cardano_preprod_wallet_name,

src/envConfig.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ export const asEnvConfig = asObject({
116116
AZTECO_API_KEY: asNullable(asString),
117117
STAKEKIT_API_KEY: asNullable(asString),
118118
KILN_TESTNET_API_KEY: asNullable(asString),
119+
KILN_TESTNET_ACCOUNT_ID: asNullable(asString),
119120
KILN_MAINNET_API_KEY: asNullable(asString),
121+
KILN_MAINNET_ACCOUNT_ID: asNullable(asString),
120122

121123
// Core plugin options:
122124
ARBITRUM_INIT: asCorePluginInit(asEvmApiKeys),

src/locales/en_US.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,6 +1414,8 @@ const strings = {
14141414
stake_break_even_days_months_s: '%1$s days (%2$s months)',
14151415
stake_earn_button_label: 'Earn',
14161416
stake_unable_to_query_locked: 'Unable to query locked balance. Please try again later.',
1417+
stake_liquid_staking_warning_title: 'Liquid Staking Pool Notice',
1418+
stake_liquid_staking_warning_header: `This is a liquid-staking pool, meaning the entire wallet's balance is either staked/unstaked in the pool. Wallet funds will remain liquid (transferable), however all current and future funds will be exposed to the risks and rewards of the staking pool while staked.`,
14171419

14181420
// Tron resource staking
14191421
stake_resource_display_name: 'TRON Resources',

src/locales/strings/enUS.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,8 @@
12421242
"stake_break_even_days_months_s": "%1$s days (%2$s months)",
12431243
"stake_earn_button_label": "Earn",
12441244
"stake_unable_to_query_locked": "Unable to query locked balance. Please try again later.",
1245+
"stake_liquid_staking_warning_title": "Liquid Staking Pool Notice",
1246+
"stake_liquid_staking_warning_header": "This is a liquid-staking pool, meaning the entire wallet's balance is either staked/unstaked in the pool. Wallet funds will remain liquid (transferable), however all current and future funds will be exposed to the risks and rewards of the staking pool while staked.",
12451247
"stake_resource_display_name": "TRON Resources",
12461248
"stake_resource_display_name_v2": "TRON Resources v2",
12471249
"stake_resource_bandwidth": "Bandwidth",

src/plugins/stake-plugins/generic/GenericStakePlugin.ts

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ChangeQuote, ChangeQuoteRequest, StakePlugin, StakePluginFactory, StakePolicy, StakePolicyFilter, StakePosition, StakePositionRequest } from '../types'
2+
import { CardanoPooledKilnAdapterConfig, makeCardanoKilnAdapter } from './policyAdapters/CardanoKilnAdaptor'
23
import { CoreumNativeSkateKitAdapterConfig, makeSkateKitAdapter } from './policyAdapters/CoreumStakeKitAdaptor'
3-
import { EthereumPooledKilnAdapterConfig, makeKilnAdapter } from './policyAdapters/EthereumKilnAdaptor'
4+
import { EthereumPooledKilnAdapterConfig, makeEthereumKilnAdapter } from './policyAdapters/EthereumKilnAdaptor'
45
import { GlifInfinityPoolAdapterConfig, makeGlifInfinityPoolAdapter } from './policyAdapters/GlifInfinityPoolAdapter'
56
import { makeTarotPoolAdapter, TarotPoolAdapterConfig } from './policyAdapters/TarotPoolAdaptor'
67
import { StakeAdapterConfig, StakePolicyAdapter } from './policyAdapters/types'
@@ -69,30 +70,20 @@ export const makeGenericStakePlugin =
6970
return instance
7071
}
7172

72-
function isPolicyInfoForGlifInfinityPool(policyInfo: StakePolicyConfig<StakeAdapterConfig>): policyInfo is StakePolicyConfig<GlifInfinityPoolAdapterConfig> {
73-
return policyInfo.adapterConfig.type === 'glif-infinity-pool'
74-
}
75-
function isPolicyInfoForTarotPool(policyInfo: StakePolicyConfig<StakeAdapterConfig>): policyInfo is StakePolicyConfig<TarotPoolAdapterConfig> {
76-
return policyInfo.adapterConfig.type === 'tarot-velodrome-pool'
77-
}
78-
function isPolicyInfoForCoreumStakeKit(policyInfo: StakePolicyConfig<StakeAdapterConfig>): policyInfo is StakePolicyConfig<CoreumNativeSkateKitAdapterConfig> {
79-
return policyInfo.adapterConfig.type === 'coreum-native-stake-kit'
80-
}
81-
function isPolicyInfoForEthereumKiln(policyInfo: StakePolicyConfig<StakeAdapterConfig>): policyInfo is StakePolicyConfig<EthereumPooledKilnAdapterConfig> {
82-
return policyInfo.adapterConfig.type === 'ethereum-pooled-kiln'
83-
}
84-
8573
const makePolicyAdapter = (policyInfo: StakePolicyConfig<StakeAdapterConfig>): StakePolicyAdapter => {
86-
if (isPolicyInfoForGlifInfinityPool(policyInfo)) {
87-
return makeGlifInfinityPoolAdapter(policyInfo)
88-
} else if (isPolicyInfoForTarotPool(policyInfo)) {
89-
return makeTarotPoolAdapter(policyInfo)
90-
} else if (isPolicyInfoForCoreumStakeKit(policyInfo)) {
91-
return makeSkateKitAdapter(policyInfo)
92-
} else if (isPolicyInfoForEthereumKiln(policyInfo)) {
93-
return makeKilnAdapter(policyInfo)
94-
} else {
95-
throw new Error('Unknown policyInfo')
74+
switch (policyInfo.adapterConfig.type) {
75+
case 'cardano-pooled-kiln':
76+
return makeCardanoKilnAdapter(policyInfo as StakePolicyConfig<CardanoPooledKilnAdapterConfig>)
77+
case 'coreum-native-stake-kit':
78+
return makeSkateKitAdapter(policyInfo as StakePolicyConfig<CoreumNativeSkateKitAdapterConfig>)
79+
case 'ethereum-pooled-kiln':
80+
return makeEthereumKilnAdapter(policyInfo as StakePolicyConfig<EthereumPooledKilnAdapterConfig>)
81+
case 'glif-infinity-pool':
82+
return makeGlifInfinityPoolAdapter(policyInfo as StakePolicyConfig<GlifInfinityPoolAdapterConfig>)
83+
case 'tarot-velodrome-pool':
84+
return makeTarotPoolAdapter(policyInfo as StakePolicyConfig<TarotPoolAdapterConfig>)
85+
default:
86+
throw new Error('Unknown policyInfo')
9687
}
9788
}
9889

@@ -103,6 +94,7 @@ const makeStakePolicy = async (policyConfig: StakePolicyConfig<StakeAdapterConfi
10394
hideUnstakeAction = false,
10495
hideUnstakeAndClaimAction = false,
10596
isStablePool,
97+
isLiquidStaking,
10698
stakeProviderInfo,
10799
stakeAssets,
108100
rewardAssets,
@@ -124,6 +116,7 @@ const makeStakePolicy = async (policyConfig: StakePolicyConfig<StakeAdapterConfi
124116
hideClaimAction,
125117
hideUnstakeAction,
126118
hideUnstakeAndClaimAction,
119+
isLiquidStaking,
127120
yieldType,
128121
stakeAssets,
129122
rewardAssets,
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import { kilncardanopool } from './pluginInfo/cardanoKilnPool'
12
import { coreumnative } from './pluginInfo/coreumNativeStaking'
23
import { kilnpool } from './pluginInfo/ethereumKilnPool'
34
import { glifpoolCalibration } from './pluginInfo/filecoinCalibrationGlifpool'
45
import { glifpool } from './pluginInfo/filecoinGlifpool'
56
import { tarotpool } from './pluginInfo/optimismTarotPool'
67

7-
export const genericPlugins = [glifpool, glifpoolCalibration, tarotpool, coreumnative, kilnpool]
8+
export const genericPlugins = [glifpool, glifpoolCalibration, tarotpool, coreumnative, kilnpool, kilncardanopool]
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
import { ENV } from '../../../../env'
2+
import { CardanoPooledKilnAdapterConfig } from '../policyAdapters/CardanoKilnAdaptor'
3+
import { StakePluginInfo, StakePolicyConfig } from '../types'
4+
5+
const kilnPolicyConfig: Array<StakePolicyConfig<CardanoPooledKilnAdapterConfig>> = [
6+
{
7+
stakePolicyId: 'cardano_kiln_pool0',
8+
stakeProviderInfo: {
9+
displayName: 'Cardano Staking Pool (KILN0)',
10+
pluginId: 'cardano',
11+
stakeProviderId: 'cardano_pooled_kiln'
12+
},
13+
parentPluginId: 'cardano',
14+
parentCurrencyCode: 'ADA',
15+
adapterConfig: {
16+
type: 'cardano-pooled-kiln',
17+
pluginId: 'cardano',
18+
19+
accountId: ENV.KILN_MAINNET_ACCOUNT_ID,
20+
apiKey: ENV.KILN_MAINNET_API_KEY,
21+
baseUrl: 'https://api.kiln.fi',
22+
poolId: 'pool10rdglgh4pzvkf936p2m669qzarr9dusrhmmz9nultm3uvq4eh5k'
23+
},
24+
hideUnstakeAndClaimAction: true,
25+
isLiquidStaking: true,
26+
stakeAssets: [{ pluginId: 'cardano', currencyCode: 'ADA' }],
27+
rewardAssets: [{ pluginId: 'cardano', currencyCode: 'ADA' }]
28+
},
29+
{
30+
stakePolicyId: 'cardano_kiln_pool1',
31+
stakeProviderInfo: {
32+
displayName: 'Cardano Staking Pool (KILN1)',
33+
pluginId: 'cardano',
34+
stakeProviderId: 'cardano_pooled_kiln'
35+
},
36+
parentPluginId: 'cardano',
37+
parentCurrencyCode: 'ADA',
38+
adapterConfig: {
39+
type: 'cardano-pooled-kiln',
40+
pluginId: 'cardano',
41+
42+
accountId: ENV.KILN_MAINNET_ACCOUNT_ID,
43+
apiKey: ENV.KILN_MAINNET_API_KEY,
44+
baseUrl: 'https://api.kiln.fi',
45+
poolId: 'pool1fcp4d2pxh0e7q5ju63sjqcdpxpr3pvxg6ykl23t6c97d7dnvjvw'
46+
},
47+
hideUnstakeAndClaimAction: true,
48+
isLiquidStaking: true,
49+
stakeAssets: [{ pluginId: 'cardano', currencyCode: 'ADA' }],
50+
rewardAssets: [{ pluginId: 'cardano', currencyCode: 'ADA' }]
51+
},
52+
{
53+
stakePolicyId: 'cardano_kiln_pool2',
54+
stakeProviderInfo: {
55+
displayName: 'Cardano Staking Pool (KILN2)',
56+
pluginId: 'cardano',
57+
stakeProviderId: 'cardano_pooled_kiln'
58+
},
59+
parentPluginId: 'cardano',
60+
parentCurrencyCode: 'ADA',
61+
adapterConfig: {
62+
type: 'cardano-pooled-kiln',
63+
pluginId: 'cardano',
64+
65+
accountId: ENV.KILN_MAINNET_ACCOUNT_ID,
66+
apiKey: ENV.KILN_MAINNET_API_KEY,
67+
baseUrl: 'https://api.kiln.fi',
68+
poolId: 'pool1v62c7d92xv6gyh4x9rhfpkwzlpw2ypxk92xvzavakg3xypatklv'
69+
},
70+
hideUnstakeAndClaimAction: true,
71+
isLiquidStaking: true,
72+
stakeAssets: [{ pluginId: 'cardano', currencyCode: 'ADA' }],
73+
rewardAssets: [{ pluginId: 'cardano', currencyCode: 'ADA' }]
74+
},
75+
{
76+
stakePolicyId: 'cardano_kiln_pool3',
77+
stakeProviderInfo: {
78+
displayName: 'Cardano Staking Pool (KILN3)',
79+
pluginId: 'cardano',
80+
stakeProviderId: 'cardano_pooled_kiln'
81+
},
82+
parentPluginId: 'cardano',
83+
parentCurrencyCode: 'ADA',
84+
adapterConfig: {
85+
type: 'cardano-pooled-kiln',
86+
pluginId: 'cardano',
87+
88+
accountId: ENV.KILN_MAINNET_ACCOUNT_ID,
89+
apiKey: ENV.KILN_MAINNET_API_KEY,
90+
baseUrl: 'https://api.kiln.fi',
91+
poolId: 'pool1mtxmk0skqkr5y0wxnxps4n35j6wn9q8dfr82y423vvlp53vccux'
92+
},
93+
hideUnstakeAndClaimAction: true,
94+
isLiquidStaking: true,
95+
stakeAssets: [{ pluginId: 'cardano', currencyCode: 'ADA' }],
96+
rewardAssets: [{ pluginId: 'cardano', currencyCode: 'ADA' }]
97+
},
98+
{
99+
stakePolicyId: 'cardano_kiln_pool4',
100+
stakeProviderInfo: {
101+
displayName: 'Cardano Staking Pool (KILN4)',
102+
pluginId: 'cardano',
103+
stakeProviderId: 'cardano_pooled_kiln'
104+
},
105+
parentPluginId: 'cardano',
106+
parentCurrencyCode: 'ADA',
107+
adapterConfig: {
108+
type: 'cardano-pooled-kiln',
109+
pluginId: 'cardano',
110+
111+
accountId: ENV.KILN_MAINNET_ACCOUNT_ID,
112+
apiKey: ENV.KILN_MAINNET_API_KEY,
113+
baseUrl: 'https://api.kiln.fi',
114+
poolId: 'pool10d6mmw3mn9ku3r7uqqye672dz3sv76lh5kvh5rdpr9l5ug5yknr'
115+
},
116+
hideUnstakeAndClaimAction: true,
117+
isLiquidStaking: true,
118+
stakeAssets: [{ pluginId: 'cardano', currencyCode: 'ADA' }],
119+
rewardAssets: [{ pluginId: 'cardano', currencyCode: 'ADA' }]
120+
},
121+
{
122+
stakePolicyId: 'cardano_kiln_pool6',
123+
stakeProviderInfo: {
124+
displayName: 'Cardano Staking Pool (KILN6)',
125+
pluginId: 'cardano',
126+
stakeProviderId: 'cardano_pooled_kiln'
127+
},
128+
parentPluginId: 'cardano',
129+
parentCurrencyCode: 'ADA',
130+
adapterConfig: {
131+
type: 'cardano-pooled-kiln',
132+
pluginId: 'cardano',
133+
134+
accountId: ENV.KILN_MAINNET_ACCOUNT_ID,
135+
apiKey: ENV.KILN_MAINNET_API_KEY,
136+
baseUrl: 'https://api.kiln.fi',
137+
poolId: 'pool1mtuhuh8hkf8am0qzx45y58kll8q83sjh6pwljrflcmw970d82f3'
138+
},
139+
hideUnstakeAndClaimAction: true,
140+
isLiquidStaking: true,
141+
stakeAssets: [{ pluginId: 'cardano', currencyCode: 'ADA' }],
142+
rewardAssets: [{ pluginId: 'cardano', currencyCode: 'ADA' }]
143+
},
144+
{
145+
stakePolicyId: 'cardano_kiln_pool7',
146+
stakeProviderInfo: {
147+
displayName: 'Cardano Staking Pool (KILN7)',
148+
pluginId: 'cardano',
149+
stakeProviderId: 'cardano_pooled_kiln'
150+
},
151+
parentPluginId: 'cardano',
152+
parentCurrencyCode: 'ADA',
153+
adapterConfig: {
154+
type: 'cardano-pooled-kiln',
155+
pluginId: 'cardano',
156+
157+
accountId: ENV.KILN_MAINNET_ACCOUNT_ID,
158+
apiKey: ENV.KILN_MAINNET_API_KEY,
159+
baseUrl: 'https://api.kiln.fi',
160+
poolId: 'pool1aqg8vxzv75zhjzjjd9s20fu6r0xz70yl8lk3teacwy7qyc2p2j7'
161+
},
162+
hideUnstakeAndClaimAction: true,
163+
isLiquidStaking: true,
164+
stakeAssets: [{ pluginId: 'cardano', currencyCode: 'ADA' }],
165+
rewardAssets: [{ pluginId: 'cardano', currencyCode: 'ADA' }]
166+
},
167+
{
168+
stakePolicyId: 'cardano_kiln_pool8',
169+
stakeProviderInfo: {
170+
displayName: 'Cardano Staking Pool (KILN8)',
171+
pluginId: 'cardano',
172+
stakeProviderId: 'cardano_pooled_kiln'
173+
},
174+
parentPluginId: 'cardano',
175+
parentCurrencyCode: 'ADA',
176+
adapterConfig: {
177+
type: 'cardano-pooled-kiln',
178+
pluginId: 'cardano',
179+
180+
accountId: ENV.KILN_MAINNET_ACCOUNT_ID,
181+
apiKey: ENV.KILN_MAINNET_API_KEY,
182+
baseUrl: 'https://api.kiln.fi',
183+
poolId: 'pool19kfm6lz5uw7nylq27swr367mqdycmug7tve94l6h3xsz64seqtc'
184+
},
185+
hideUnstakeAndClaimAction: true,
186+
isLiquidStaking: true,
187+
stakeAssets: [{ pluginId: 'cardano', currencyCode: 'ADA' }],
188+
rewardAssets: [{ pluginId: 'cardano', currencyCode: 'ADA' }]
189+
}
190+
]
191+
192+
export const kilncardanopool: StakePluginInfo = {
193+
pluginId: 'stake:cardano:kiln',
194+
policyConfigs: [...kilnPolicyConfig]
195+
}

0 commit comments

Comments
 (0)