@kresuslabs/core v2.11.6
Kresus Core SDK
Name
kresus-core
Installation
install with npm:
npm install @kresuslabs/core --save
install with yarn:
yarn add @kresuslabs/core
Creating Provider with Magic Link
import { ethers } from "ethers";
import { Magic } = require("magic-sdk");
/*
Ethereum
*/
const magicEth = new Magic("your_magic_api_key", {network: {
rpcUrl: "RPC_URL_FOR_ETHEREUM",
chainId: 5 // goerli
}});
// or
const magicEth = new Magic("your_magic_api_key", {network: 'goerli'});
await magicEth.auth.loginWithMagicLink({email: "youremail@example.com"});
const provider = new ethers.providers.Web3Provider(magicEth.rpcProvider);
(await magicEth.user.getMetadata()).publicAddress; // get public address
Vault Operations
import { VaultRegistry, CoreEnv } from "@kresuslabs/core";
const configVaultRegistry = {
env: CoreEnv.dev,
orgId: "Kresus",
rpcKey: "AlchemyRpcKey"
};
const vaultRegistryObj = new VaultRegistry(configVaultRegistry);
Add Vault:
timeDelay = 1000;
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
await vaultRegistryObj.addVault(timeDelay, bioAuthToken, authToken);
Deploy Vault:
vaultId = "04fde114-ab47-4933-a764-73bc3a24f93c";
identityId?="test"
authToken = "AUTHENTICATION_TOKEN";
await vaultRegistryObj.deployVault(vaultId,authToken,identityId);
import { Vault, CoreEnv } from "@kresuslabs/core";
const configVault = {
env: CoreEnv.dev,
vaultAddr: "0x14F6559416101FFc9A32Bb35C8f90127A4E2CEF8",
vaultId: "43d4ecfb-8bb6-4a47-804b-9d5b2b884862",
chainId: 5, // goerli
orgId: "Kresus",
rpcKey: "https://polygon-mumbai.g.alchemy.com/v2/<APIKEY>"
};
const vaultObj = new Vault(configVault, provider);
Transfer ETH from Vault:
toAddress = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
amount = "0.1";
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.transferETHFromVault(toAddress, amount, bioAuthToken, authToken, credits);
Transfer ERC20 from Vault:
toAddress = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
amount = "1999999999999999999";
erc20ContractAddress = "0xb5B0cDBf3b42DDEBfaA1Fe95A79d29854F325eD3";
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.transferERC20FromVault(toAddress, amount, erc20ContractAddress, bioAuthToken, authToken, credits);
Transfer ERC721 from Vault:
toAddress = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
amount = "1";
erc721ContractAddress = "0xa6C0cGAf1d93AAEBfaA1Fe95A79d29854F325eD3";
tokenId = "9876";
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.transferERC721FromVault(toAddress, amount, erc721ContractAddress, tokenId, bioAuthToken, authToken, credits);
Transfer ERC1155 from Vault:
toAddress = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
amount = "1";
erc1155ContractAddress = "0x55B0cDBf3b42DDEBfaA1Fe98B79d71298A987eD3";
tokenId = "5678";
data = "0x";
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.transferERC1155FromVault(toAddress, amount, erc1155ContractAddress, tokenId, data, bioAuthToken, authToken, credits);
Bulk Withdraw assests from Vault:
toAddr = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
ethAmount = "100000000000000"
erc20Params = [{
contractAddress : "0x07865c6E87B9F70255377e024ace6630C1Eaa37F",
amount : "10000"
},
{
contractAddress : "0x326c977e6efc84e512bb9c30f76e30c160ed06fb",
amount : "100000000000000"
}]
erc721Params = [{
contractAddress : "0xbC40FB08C4F6AE0BD91c11965D8E37FA90ecccEf",
tokenId : "4"
},
{
contractAddress : "0xbC40FB08C4F6AE0BD91c11965D8E37FA90ecccEf",
tokenId : "5"
}]
erc1155Params = [{
contractAddress : "0x75Bd1BC1757eAe47E0Ef14CddaA8d5B08207b76C",
tokenId : "0",
amount : "1",
data : "0x"
},
{
contractAddress : "0x75Bd1BC1757eAe47E0Ef14CddaA8d5B08207b76C",
tokenId : "1",
amount : "2",
data : "0x"
}]
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.bulkWithdrawal(toAddr,ethAmount,erc20Params,erc721Params,erc1155Params,bioAuthToken,authToken, credits)
Set Time Delay:
newTimeDelay = "600";
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.setTimeDelay(newTimeDelay, bioAuthToken, authToken, credits);
lock User Vault:
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.lock(bioAuthToken, authToken);
Unlock User Vault:
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.unlock(bioAuthToken, authToken, credits);
Transfer Ownership of Vault:
newOwner = "0x5D89f57e0184708A3530d0C8ef3dcF4614e3fcc5";
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.transferOwnership(newOwner, bioAuthToken, authToken, credits);
Add Guardian:
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.addGuardian(bioAuthToken, authToken, credits);
Add Trustee:
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.addTrustee(bioAuthToken, authToken, credits);
Remove Guardian:
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.removeGuardian(bioAuthToken, authToken, credits);
Remove Trustee:
bioAuthToken = "BIOMETRIC_AUTH_TOKEN";
authToken = "AUTHENTICATION_TOKEN";
credits = 10;
await vaultObj.removeTrustee(bioAuthToken, authToken, credits);
Cancel Operation:
operationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.cancelOperation(operationId,authToken);
Reject Operation:
operationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.rejectOperation(operationId,authToken);
Increase Allowance:
operationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
offset= 2;
isCancel=true;
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.increaseAllowance(operationId,offset,isCancel, provider, authToken);
Get Operation:
vaultId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.getOperations(vaultId,authToken);
Remind Operation:
operationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
guardianAddress = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.remindOperation(operationId,guardianAddress,authToken);
Get Operation Details:
operationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.getOperationDetails(operationId,authToken);
getVaultList:
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.getVaultList(authToken);
getVaultsAmGuarding:
status = "active";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.getVaultsAmGuarding(authToken, status);
getTrustorVaults:
status = "active";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.getTrustorVaults(authToken, status);
removeAllInvites:
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.removeAllInvites(authToken);
Accept Operation:
requestId = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
authToken = "AUTHENTICATION_TOKEN";
bioAuthToken? = "BIOAUTHTOKEN"
await vaultObj.acceptOperation(requestId, authToken, bioAuthToken);
Invite Guardian:
guardianAddress = ["0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5"];
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.inviteGuardians(guardianAddress, authToken);
Accept Invitation:
invitationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.acceptInvitation(invitationId, authToken);
Reject Invitation:
invitationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.rejectInvitation(invitationId, authToken);
Extend Invitation:
invitationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.extendInvitation(invitationId, authToken);
Replace Invitation:
inviteAddress = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
invitationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.replaceInvitation(inviteAddress,invitationId, authToken);
isUnlockPaymentDone:
timestamp = '2023-11-25T10:16:08.616Z';
auth = "AUTHENTICATION_TOKEN";
await vaultObj.isUnlockPaymentDone(timestamp,auth);
checkTokenOwnership:
contractAddress = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
auth = "AUTHENTICATION_TOKEN";
await vaultObj.checkTokenOwnership(contractAddress,auth);
closeVault:
vaultId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
auth = "AUTHENTICATION_TOKEN";
await vaultObj.closeVault(vaultId,auth);
Get Invitation details:
invitationId = "396ae6ea-0229-4b9c-9885-8a223ce5b5b5";
authToken = "AUTHENTICATION_TOKEN";
await vaultObj.getInvitationDetails(invitationId, authToken);
Get Heir:
await vaultObj.getHeir( provider);
Get TimeDelay:
await vaultObj.getTimeDelay( provider);
Check if Vault is Locked:
await vaultObj.isLocked( provider);
Check if Voting is Enabled:
await vaultObj.isVotingEnabled( provider);
Get Vault Details:
await vaultObj.getVaultDetails( provider);
Get Vault History:
authToken = "AUTHENTICATION_TOKEN";
ownerAddress = "0x1C67f57e0184708A3530d0C8ef3dcF4614e3edd5";
recordCount = "10";
await vaultObj.accept(authToken, ownerAddress, recordCount, provider);
Invite Trustee
let trusteeAddress = "0xdc89dbc543f8b83f92760db79b4988f0c4433ef8";
await vaultObj.inviteTrustee([trusteeAddress],<AUTH_TOKEN>);
Cancel Invitation
let invitationId = "1dfce4d7-bd78-4c7f-aaca-30275361eef5");
await vaultObj.cancelInvitation(invitationId,<AUTH_TOKEN>)
Remind Invitation
let invitationId = "1dfce4d7-bd78-4c7f-aaca-30275361eef5");
await vaultObj.remindInvitation(invitationId,<AUTH_TOKEN>)
Get Operation Requirement
let Operation = "multiCall";
await vaultObj.getOperationRequirement(<AUTH_TOKEN>, Operation)
Usage for ENS
import {Ens} from "@kresuslabs/core";
const ens = new Ens();
Get ENS Name from Address:
const address = "0x3E0e5C49008c6Ef7CF08a7c458C50498E1455851";
await ens.getEnsName(address, provider);
Get Address from ENS name:
const ensName = "rizzy" // or "rizzy.eth";
await ens.getEnsAddress(ensName, provider);
Check ENS Name Availability:
const checkName = "rizzy" // do not send "rizzy.eth";
await ens.checkAvailable(checkName, provider);
Usage for Airdrop
import {Airdrop} from "@kresuslabs/core";
const config = {
contractAddress: "0x3E0e5C49008c6Ef7CF08a7c458C50498E1455851"
}
const airdrop = new Airdrop(config);
Check if user has already claimed or not:
const userAddress = "0x3E0e5C49008c6Ef7CF08a7c458C50498E1455851";
const tokenId = "1";
await airdrop.hasClaimed(userAddress, tokenId, provider);
Mint:
const maxAmount = "1000";
const tokenId = "1";
const data = "0x";
const tokenURI = "https://kresus.mypinata.cloud/ipfs/QmZjDaTxgrSh1G8kMuzWkygCcUgaTseFHQKjENDhkSk1x1";
await airdrop.mintAirdropNFT(maxAmount, tokenId, data, tokenURI, wallet);
Transfer:
const to = "0x3E0e5C49008c6Ef7CF08a7c458C50498E1455851";
const tokenId = "1";
const data = "0x";
await airdrop.transferAirdropNFT(to, tokenId, data, wallet);
Usage for NFT-Lens
import {NFTLens,CoreEnv} from "@kresuslabs/core";
const orgId = "Kresus"
const nftLens = new NFTLens(CoreEnv.dev,orgId);
createNFTLens:
const myAddress = "0xe9F743f2649eAD27C15F98fe669DE9653B25c763";
const toAddresses = ["0x20B6Ac97be081df9add2A2865A6b56cd2C05D664"];
const toEmail = ["email@xyz.com"]
const toPhone = ["+41-76542980"]
const tokenMetadata = "tokenMetadatUrl"
const authToken = "AUTH_TOKEN"
await nftLens.createNFTLens(myAddress,toAddresses,toEmail,toPhone,tokenMetadata,authToken);
Txn Note Operations
Import class and creating an instance:
import { TxnNote, CoreEnv } from "@kresuslabs/core";
const configNotes = {
env: CoreEnv.dev;
}
const txnNoteObj = new TxnNote(configNotes);
Insert Txn Record:
/*
chain_name: eth or poly or sol or btc
ack_status: received or not_received
*/
const record = {
txn_id: "78612qwerty",
chain_name: "eth",
pvt_note: "SEND me 5 Eth",
pub_note: "5 eth sent",
ack_status: "not_received",
token_usd_rate: 5,
native_token_usd_rate: 11,
from_addr: "0xbabcuddu",
to_addr: "aisic8dudd"
};
const orgId = "Kresus"; //from IAM
const authToken = "AUTHENTICATION_TOKEN"; //from IAM
await txnNoteObj.insertRecord(record, authToken, orgId);
Update Txn Record (ack_status):
const txnId = "0x28ada8b6fe6fb3474e4182e0abc328be32200ab4d49f983c52abe172cd403142";
const chainName = "eth";
const orgId = "Kresus"; //from IAM
const authToken = "AUTHENTICATION_TOKEN"; //from IAM
const updateObj = {
ack_status: "received"
}
await txnNoteObj.updateRecord(txnId, chainName, updateObj, authToken, orgId);
Get Txn Record(s):
/*
searchKey could be a txn_id or any keyword
present in public or private note
*/
const searchKey = "0x28ada8b6fe6fb3474e4182e0abc328be32200ab4d49f983c52abe172cd403142";
const orgId = "Kresus"; //from IAM
const authToken = "AUTHENTICATION_TOKEN"; //from IAM
await txnNoteObj.getRecord(searchKey, authToken, orgId);
Get All the Txns from given Public Address:
/*
Addresses (From or To) could be of different chains (Ethereum, Polygon, Solana, Bitcoin)
*/
const addressArray = [
"0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5",
"0xc90e79c4b57419b063d4cd757c469c1acf646e5c",
"Beyru8enwLBaknXt1GHhsvDii71qiGXfL2sZyQ1zgnGQ"
];
const orgId = "Kresus"; //from IAM
const authToken = "AUTHENTICATION_TOKEN"; //from IAM
await txnNoteObj.getAllTxnsFromPubAddr(addressArray, authToken, orgId);
Swap Operations
Create a Swap Transaction
import { Swap as SwapClass } from "@kresuslabs/core/lib";
let swap = new SwapClass({env : "prod"});
from = "btc" ;
to = "eth" ;
address = "0x5b63169154C98D6829E3f5264A01ad25b1aa9cc4" ;
amountFrom = "0.05" ;
await swap.createSwap(from, to, address, amountFrom);
Get Currencies List
await swap.getCurrencies()
Get Currencies Full List
await swap.getCurrenciesFull()
Get Exchange Amount
from = "btc" ;
to = "eth" ;
amountFrom = "0.05" ;
await swap.getExchangeAmount(from, to, amountFrom)
Get Transaction Status
id = "303gz6b3ll3dvh24";
await swap.getTransactionsWithId(id)
Get Fixed Rate For Amount
InputArray = [{
"from": "eth",
"to": "btc",
"amountFrom": "5.2"
}]
await swap.getFixRateForAmount(InputArray)
Validate Address
currency = "btc",
address = "<<btc address>>"
await swap.validateAddress(currency, address)
Get Minimum Amount
from = "btc";
to = "eth";
await swap.getMinimumAmount(from, to)
Record Transactions to Proxy
userId = "<<userID>>";
swapTxId = "<<swapTxId>>" ;
swapFrom = "<<swapFrom>>" ;
swapTo = "<<swapTo>>";
txHash = "<<txHash>>";
receiveAddress = "<<receiveAddress>>";
receiveAmount = "<<receiveAmount>>";
sendAmount = "<<sendAmount>>";
await swap.recordTransactionsToProxy(userId, swapTxId, swapFrom, swapTo, txHash, receiveAddress, receiveAmount, sendAmount)
Get Transactions with ID
swapTxId = "<<swapTxId>>" ;
await swap.getStatusWithId(swapTxId) ;
Get Transaction Pending Transaction
userId = "<<userID>>";
await swap.isTxPending(swapTxId) ;
Stake Unstake
const ethAddr = "0x14F6559416101FFc9A32Bb35C8f90127A4E2CEF8";
const rpcApiKey = "alchemy-key-here";
const env = CoreEnv.uat // CoreEnv.prod for production;
const solAddr = "FnTFBoJCiS3XHS2EV1ACECUtGWpHqAW4GZ9qXKUMCURL";
const rpcApiKeySol = "shyft-key-here";
const alchemySolKey = "alchemy-key-sol-here";
const su = new StakeUnstake(
ethAddr,
rpcApiKey,
env,
provider,
solAddr,
rpcApiKeySol,
solMagic,
alchemySolKey
);
Stake ETH:
const amountInEth = "0.01";
await su.eth.stake(amountInEth);
Unstake ETH:
const amountInStEth = "0.01";
await su.eth.unstake(amountInStEth);
Withdraw ETH:
const tokenIds = ["1", "2"];
await su.eth.withdraw(tokenIds);
Get Max Withdrawal Amount:
await su.eth.getMaxWithdrawalAmount();
Get Min Withdrawal Amount:
await su.eth.getMinWithdrawalAmount();
Get Nonce:
await su.eth.getNonce();
Get Staking Info for ETH:
await su.eth.getStakingInfo();
Get Withdrawal Requests:
await su.eth.getWithdrawalRequests();
Get Withdrawal Status:
await su.eth.getWithdrawalStatus();
Instant Unstake using Uniswap:
const amountInStEth = "0.01";
await su.eth.instantUnstake(amountInStEth);
Is Allowance Sufficient:
const amountInStEth = "0.01";
await su.eth.isAllowanceSufficient(amountInStEth);
Is Staking Paused:
await su.eth.isPaused();
Rewards Calculation:
await su.eth.rewardsCalculation(provider);
Stake SOL:
const amountInSol = 0.01;
await su.sol.stake(amountInSol);
Unstake SOL:
const amountInStSol = 0.01;
await su.sol.unstake(amountIn);
Withdraw SOL:
const amountSol = 0.01;
const deactivatingSolAddr = "FnTFBoJCiS3XHS2EV1ACECUtGWpHqAW4GZ9qXKUMCURL";
await su.sol.withdraw(amountInSol, deactivatingSolAddr);
Get Stake Accounts:
await su.sol.getStakeAccounts();
Get Staking Info for SOL:
await su.sol.getStakingInfo();
License
MIT
22 days ago
1 month ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
3 months ago
3 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
4 months ago
4 months ago
4 months ago
3 months ago
4 months ago
3 months ago
3 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
7 months ago
9 months ago
10 months ago
10 months ago
10 months ago
10 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
12 months ago
12 months ago
12 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago