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

A-1207203856760647: staking #118

Merged
merged 11 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: 16.14.2
node-version: 20.9.0

# ESLint and Prettier must be in `package.json`
- name: Install Node.js dependencies
Expand Down
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node-options="--openssl-legacy-provider"
26 changes: 26 additions & 0 deletions Navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ import LdkViewLogs from './screen/wallets/ldkViewLogs';
import Introduction from './screen/introduction';
import NativeAssets from './screen/NativeAssets';

import Staking from './screen/staking/staking';
import StakingDelegationDetails from './screen/staking/delegationDetails';
import StakingConfirm from './screen/staking/confirm';
import StakingSuccess from './screen/staking/success';

import { sendBottom, settingBottom, walletBottom, create_wallet, ic_back_black } from './theme/Images';
import { COLORS } from './theme/Colors';
import { getFlage } from './store/asyncStorage';
Expand Down Expand Up @@ -214,6 +219,20 @@ const AddWalletRoot = () => {
);
};

const StakingStack = createNativeStackNavigator();

const StakingRoot = () => {
const theme = useTheme();

return (
<StakingStack.Navigator screenOptions={{ headerHideShadow: true }}>
<StakingStack.Screen name="Staking" component={Staking} options={Staking.navigationOptions(theme)} />
<StakingStack.Screen name="Delegation" component={StakingDelegationDetails} options={Staking.navigationOptions(theme)} />
<StakingStack.Screen name="Confirm" component={StakingConfirm} options={Staking.navigationOptions(theme)} />
</StakingStack.Navigator>
);
};

// CreateTransactionStackNavigator === SendDetailsStack
const SendDetailsStack = createNativeStackNavigator();
const SendDetailsRoot = () => {
Expand Down Expand Up @@ -559,6 +578,7 @@ function MlWalletRoot() {
<TopTab.Navigator tabBarOptions={tabBarOptions}>
<TopTab.Screen name="Transactions" component={MLWalletTransactions} initialParams={{ walletID }} />
<TopTab.Screen name="Tokens" component={MLWalletTokens} initialParams={{ walletID }} />
<TopTab.Screen name="Stake ML" component={Staking} initialParams={{ walletID }} />
</TopTab.Navigator>
</View>
);
Expand Down Expand Up @@ -606,6 +626,10 @@ const Navigation = () => {
<RootStack.Screen name="RBFBumpFee" component={RBFBumpFee} options={RBFBumpFee.navigationOptions(theme)} />
<RootStack.Screen name="RBFCancel" component={RBFCancel} options={RBFCancel.navigationOptions(theme)} />

<StakingStack.Screen name="Delegation" component={StakingDelegationDetails} options={Staking.navigationOptions(theme)} />
<StakingStack.Screen name="Confirm" component={StakingConfirm} options={Staking.navigationOptions(theme)} />
<StakingStack.Screen name="Success" component={StakingSuccess} options={{ headerShown: false, gestureEnabled: false }} />

<RootStack.Screen
name="ScanQRCodeRoot"
component={ScanQRCodeRoot}
Expand All @@ -614,6 +638,8 @@ const Navigation = () => {
stackPresentation: isDesktop ? 'containedModal' : 'fullScreenModal',
}}
/>

<RootStack.Screen name="StakingRoot" component={StakingRoot} options={NavigationDefaultOptions} />
</RootStack.Navigator>
);
};
Expand Down
17 changes: 15 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,19 @@ project.ext.react = [
]

apply from: "../../node_modules/react-native/react.gradle"

def setNodeEnvOptions = { execSpec ->
execSpec.environment("NODE_OPTIONS", "--openssl-legacy-provider")
}

tasks.whenTaskAdded { task ->
if (task.name == 'bundleReleaseJsAndAssets') {
task.doFirst {
setNodeEnvOptions(delegate)
}
}
}

// apply plugin: "com.bugsnag.android.gradle"

/**
Expand Down Expand Up @@ -142,8 +155,8 @@ android {
applicationId "com.mojitowallet"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 41
versionName "0.3.1"
versionCode 44
versionName "0.3.2"
multiDexEnabled true
missingDimensionStrategy 'react-native-camera', 'general'
testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type
Expand Down
Binary file not shown.
2 changes: 1 addition & 1 deletion android/link-assets-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
},
{
"path": "assets/mintlayer/wasm_wrappers_bg.wasm",
"sha1": "930db0e06806224c26edd6d0c0b7c87f49000de5"
"sha1": "d379259e3524833caf75b5a8536e1d26fbc5964e"
}
]
}
Binary file modified assets/mintlayer/wasm_wrappers_bg.wasm
Binary file not shown.
52 changes: 49 additions & 3 deletions blue_modules/Mintlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ const MINTLAYER_ENDPOINTS = {
GET_DELEGATION: '/delegation/:delegation',
GET_CHAIN_TIP: '/chain/tip',
GET_TOKEN_DATA: '/token/:token',
GET_BLOCK_HASH: '/chain/:height',
GET_BLOCK_DATA: '/block/:hash',
GET_POOL_DATA: '/pool/:pool_id',
};

const ML_NETWORK_TYPES = {
Expand Down Expand Up @@ -100,8 +103,41 @@ const getWalletUtxos = (addresses) => {
return Promise.all(utxosPromises);
};

const getChainTip = async () => {
return tryServers(MINTLAYER_ENDPOINTS.GET_CHAIN_TIP);
const getAddressDelegations = (address, network) => {
const endpoint = MINTLAYER_ENDPOINTS.GET_ADDRESS_DELEGATIONS.replace(':address', address);
return tryServers({ endpoint, network });
};

const getDelegation = (delegation, network) => {
const endpoint = MINTLAYER_ENDPOINTS.GET_DELEGATION.replace(':delegation', delegation);
return tryServers({ endpoint, network });
};

const getBlockDataByHeight = (height, network) => {
const endpoint = MINTLAYER_ENDPOINTS.GET_BLOCK_HASH.replace(':height', height);
return tryServers({ endpoint, network })
.then(JSON.parse)
.then((response) => {
const endpoint = MINTLAYER_ENDPOINTS.GET_BLOCK_DATA.replace(':hash', response);
return tryServers({ endpoint, network });
});
};

const getWalletDelegations = (addresses, network) => {
const delegationsPromises = addresses.map((address) => getAddressDelegations(address, network));
return Promise.all(delegationsPromises).then((results) => results.flatMap(JSON.parse));
};
const getDelegationDetails = (delegations, network) => {
const delegationsPromises = delegations.map((delegation) => getDelegation(delegation, network));
return Promise.all(delegationsPromises).then((results) => results.flatMap(JSON.parse));
};
const getBlocksData = (heights, network) => {
const heightsPromises = heights.map((height) => getBlockDataByHeight(height, network));
return Promise.all(heightsPromises).then((results) => results.flatMap(JSON.parse));
};

const getChainTip = async (network) => {
return tryServers({ endpoint: MINTLAYER_ENDPOINTS.GET_CHAIN_TIP, network });
};

const getFeesEstimates = async (network) => {
Expand All @@ -117,4 +153,14 @@ const getTokenData = async (token, network) => {
return tryServers({ endpoint, network });
};

export { TransactionType, getAddressData, getTransactionData, getAddressUtxo, getWalletUtxos, broadcastTransaction, getFeesEstimates, getChainTip, getTokenData, MINTLAYER_ENDPOINTS, ML_NETWORK_TYPES, ML_ATOMS_PER_COIN };
const getPool = async (pool_id, network) => {
const endpoint = MINTLAYER_ENDPOINTS.GET_POOL_DATA.replace(':pool_id', pool_id);
return tryServers({ endpoint, network });
};

const getPoolsData = async (pool_ids, network) => {
const poolsPromises = pool_ids.map((pool_id) => getPool(pool_id, network));
return Promise.all(poolsPromises).then((results) => results.flatMap(JSON.parse));
};

export { TransactionType, getAddressData, getTransactionData, getAddressUtxo, getWalletUtxos, broadcastTransaction, getFeesEstimates, getChainTip, getTokenData, getWalletDelegations, getBlocksData, getDelegationDetails, getPoolsData, MINTLAYER_ENDPOINTS, ML_NETWORK_TYPES, ML_ATOMS_PER_COIN };
34 changes: 17 additions & 17 deletions blue_modules/mintlayer/@mintlayerlib-js/wasm_wrappers.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,18 +398,13 @@ export enum Network {
Signet = 3,
}
/**
* Indicates whether a token can be frozen
*/
export enum FreezableToken {
No = 0,
Yes = 1,
}
/**
* A utxo can either come from a transaction or a block reward. This enum signifies that.
* The part of the transaction that will be committed in the signature. Similar to bitcoin's sighash.
*/
export enum SourceId {
Transaction = 0,
BlockReward = 1,
export enum SignatureHashType {
ALL = 0,
NONE = 1,
SINGLE = 2,
ANYONECANPAY = 3,
}
/**
* The token supply of a specific token, set on issuance
Expand All @@ -429,13 +424,18 @@ export enum TotalSupply {
Fixed = 2,
}
/**
* The part of the transaction that will be committed in the signature. Similar to bitcoin's sighash.
* Indicates whether a token can be frozen
*/
export enum SignatureHashType {
ALL = 0,
NONE = 1,
SINGLE = 2,
ANYONECANPAY = 3,
export enum FreezableToken {
No = 0,
Yes = 1,
}
/**
* A utxo can either come from a transaction or a block reward. This enum signifies that.
*/
export enum SourceId {
Transaction = 0,
BlockReward = 1,
}
/**
* Amount type abstraction. The amount type is stored in a string
Expand Down
16 changes: 8 additions & 8 deletions blue_modules/mintlayer/@mintlayerlib-js/wasm_wrappers.js
Original file line number Diff line number Diff line change
Expand Up @@ -1336,13 +1336,9 @@ function handleError(f, args) {
*/
export const Network = Object.freeze({ Mainnet: 0, 0: 'Mainnet', Testnet: 1, 1: 'Testnet', Regtest: 2, 2: 'Regtest', Signet: 3, 3: 'Signet' });
/**
* Indicates whether a token can be frozen
*/
export const FreezableToken = Object.freeze({ No: 0, 0: 'No', Yes: 1, 1: 'Yes' });
/**
* A utxo can either come from a transaction or a block reward. This enum signifies that.
* The part of the transaction that will be committed in the signature. Similar to bitcoin's sighash.
*/
export const SourceId = Object.freeze({ Transaction: 0, 0: 'Transaction', BlockReward: 1, 1: 'BlockReward' });
export const SignatureHashType = Object.freeze({ ALL: 0, 0: 'ALL', NONE: 1, 1: 'NONE', SINGLE: 2, 2: 'SINGLE', ANYONECANPAY: 3, 3: 'ANYONECANPAY' });
/**
* The token supply of a specific token, set on issuance
*/
Expand All @@ -1364,9 +1360,13 @@ export const TotalSupply = Object.freeze({
2: 'Fixed',
});
/**
* The part of the transaction that will be committed in the signature. Similar to bitcoin's sighash.
* Indicates whether a token can be frozen
*/
export const SignatureHashType = Object.freeze({ ALL: 0, 0: 'ALL', NONE: 1, 1: 'NONE', SINGLE: 2, 2: 'SINGLE', ANYONECANPAY: 3, 3: 'ANYONECANPAY' });
export const FreezableToken = Object.freeze({ No: 0, 0: 'No', Yes: 1, 1: 'Yes' });
/**
* A utxo can either come from a transaction or a block reward. This enum signifies that.
*/
export const SourceId = Object.freeze({ Transaction: 0, 0: 'Transaction', BlockReward: 1, 1: 'BlockReward' });

const AmountFinalization = typeof FinalizationRegistry === 'undefined' ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((ptr) => wasm.__wbg_amount_free(ptr >>> 0));
/**
Expand Down
39 changes: 35 additions & 4 deletions blue_modules/mintlayer/mintlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ const wasmMethods = {
encode_output_lock_then_transfer: 'encode_output_lock_then_transfer',
staking_pool_spend_maturity_block_count: 'staking_pool_spend_maturity_block_count',
encode_lock_for_block_count: 'encode_lock_for_block_count',
encode_output_create_delegation: 'encode_output_create_delegation',
encode_output_delegate_staking: 'encode_output_delegate_staking',
encode_input_for_withdraw_from_delegation: 'encode_input_for_withdraw_from_delegation',
};

export const getPrivateKeyFromMnemonic = async (mnemonic, networkType) => {
Expand Down Expand Up @@ -69,7 +72,7 @@ export const getTxInput = async (outpointSourceId, index) => {
return webviewEventBus.exec(wasmMethods.encode_input_for_utxo, [outpointSourceId, index]);
};

export const getOutputs = async ({ amount, address, networkType, type = 'Transfer', lock, chainTip, tokenId }) => {
export const getOutputs = async ({ amount, address, networkType, type = 'Transfer', lock, chainTip, tokenId, poolId, delegationId }) => {
if (type === 'LockThenTransfer' && !lock) {
throw new Error('LockThenTransfer requires a lock');
}
Expand All @@ -94,11 +97,34 @@ export const getOutputs = async ({ amount, address, networkType, type = 'Transfe
return webviewEventBus.exec(wasmMethods.encode_output_lock_then_transfer, [amount, address, lockEncoded, networkIndex]);
}
if (type === 'spendFromDelegation') {
const chainTip = await Mintlayer.getChainTip();
const stakingMaturity = await getStakingMaturity(JSON.parse(chainTip).block_height, networkType);
const encodedLockForBlock = await webviewEventBus.exec(wasmMethods.encode_lock_for_block_count, [stakingMaturity]);
const chainTip = await Mintlayer.getChainTip(networkType);
const blockHeight = JSON.parse(chainTip).block_height;
const stakingMaturity = await webviewEventBus.exec(wasmMethods.staking_pool_spend_maturity_block_count, [BigInt(blockHeight), networkIndex]);
const encodedLockForBlock = await webviewEventBus.exec(wasmMethods.encode_lock_for_block_count, [BigInt(stakingMaturity)]);
return webviewEventBus.exec(wasmMethods.encode_output_lock_then_transfer, [amount, address, encodedLockForBlock, networkIndex]);
}
if (type === 'CreateDelegationId') {
if (!poolId) {
throw new Error('Pool ID is required for CreateDelegationId');
}
return webviewEventBus.exec(wasmMethods.encode_output_create_delegation, [poolId, address, networkIndex]);
}
if (type === 'DelegateStaking') {
if (!delegationId) {
throw new Error('Delegation ID is required for CreateDelegationId');
}
return webviewEventBus.exec(wasmMethods.encode_output_delegate_staking, [amount, delegationId, networkIndex]);
}
};

export const getDelegationOutput = async ({ poolId, address, networkType }) => {
const networkIndex = NETWORKS[networkType];
return webviewEventBus.exec(wasmMethods.encode_output_create_delegation, [poolId, address, networkIndex]);
};

export const getStakingOutput = async ({ amount, delegationId, networkType }) => {
const networkIndex = NETWORKS[networkType];
return webviewEventBus.exec(wasmMethods.encode_output_delegate_staking, [amount, delegationId, networkIndex]);
};

export const getStakingMaturity = async (blockHeight, networkType) => {
Expand Down Expand Up @@ -132,3 +158,8 @@ export const getEstimatetransactionSize = async (inputs, inputAddresses, outputs
const networkIndex = NETWORKS[networkType];
return webviewEventBus.exec(wasmMethods.estimate_transaction_size, [inputs, inputAddresses, outputs, networkIndex]);
};

export const getAccountOutpointInput = async (delegationId, amount, nonce, networkType) => {
const networkIndex = NETWORKS[networkType];
return webviewEventBus.exec(wasmMethods.encode_input_for_withdraw_from_delegation, [delegationId, amount, BigInt(Number(nonce)), networkIndex]);
};
Loading
Loading