Noticed this code comment in an Anchor test:
What's the ATA stand for there and why does it matter for DAO controlled authorities?
ATA is an associated token address.
Associated token addresses can be derived from another public key using findProgramAddress.
function getAssociatedTokenAddress(
mint: PublicKey,
owner: PublicKey,
allowOwnerOffCurve = false,
programId = TOKEN_PROGRAM_ID,
associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID
): Promise<PublicKey> {
if (!allowOwnerOffCurve && !PublicKey.isOnCurve(owner.toBuffer())) throw new TokenOwnerOffCurveError();
const [address] = await PublicKey.findProgramAddress(
[owner.toBuffer(), programId.toBuffer(), mint.toBuffer()],
associatedTokenProgramId
);
return address;
}
There is another type of token account that predates the associated token account, an ancillary token account. These legacy accounts still exist and can be created today.
The reason that the code says it can be either ATA or something else is because it is validating that the account is a token account that it expects.
Related
Does the minted NFT from Metaplex(candy machine) automatically generate the Token Account (as I am planning to transfer it to other owner via Javascript). Also, i am using the function getOrCreateAssociatedTokenAccount to create a token account but my problem is it needs keypair and I dont know where to locate if I a only have a wallet address? Here is my code:
// wallet address of the owner of NFT but I want to get the keypair here since it is the one that is required for getOrCreateAssociatedTokenAccount
const fromWallet = owner_wallet;
// Public Key of buyer
const toWallet = buyer_wallet;
// NFT mint address
let mint = mint_address;
//connection
const connection = use_connection;
// Get the token account of the toWallet address, and if it does not exist, create it
const toTokenAccount = await getOrCreateAssociatedTokenAccount(connection, fromWallet, mint, toWallet);
// Get the token account of the fromWallet address, and if it does not exist, create it (my problem here is 2nd parameter is looking for keypair but i can only provide a wallet address
const fromTokenAccount = await getOrCreateAssociatedTokenAccount(
connection,
fromWallet,
mint,
fromWallet.publicKey
);
const signature = await transfer(
connection,
fromWallet,
fromTokenAccount.address,
toTokenAccount.address,
fromWallet.publicKey,
0
);
console.log(`finished transfer with ${signature}`);
Does the minted NFT from Metaplex(candy machine) automatically generate the Token Account --> Yes
(as I am planning to transfer it to other owner via Javascript) --> you need / should create a new token account and not just transfer the ownership of the token account
Also, i am using the function getOrCreateAssociatedTokenAccount to create a token account but my problem is it needs keypair and I dont know where to locate if I a only have a wallet address? --> You need to use the wallet / keypair of the wallet that is the current holder of the NFT.
This question is not related to candy machine but to transferring NFTs. Have a look here to learn how to transfer tokens: https://solanacookbook.com/references/basic-transactions.html#how-to-send-spl-tokens
I have following account Solana Explorer Account
And i can see there is a Mint account ( Account that store my tokens ) how i can get the mint account having my public key is there any relation or how this thing is working in general
Some terminology to be sure we're on the same page:
the "wallet" is the system account that owns other accounts, your linked "Solana Explorer Account" of CGP6sKHyrZGPJRoUAy8XbyzmX7YD4tVBQG9SEe9ekZM6
the mint account defines the token, and does not hold any tokens, is Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr
the account that holds your tokens is actually G6ogFW6YzBpYKhwZrckZJa4yejcjNTfHLE2mUAQFv3ic: https://explorer.solana.com/address/G6ogFW6YzBpYKhwZrckZJa4yejcjNTfHLE2mUAQFv3ic?cluster=devnet -- this is an "associated token account", whose address is derived from your wallet. The tools and explorer default to using that account when dealing with the Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr token. To get this account, you can use getAssociatedTokenAccount, which boils down to:
import { PublicKey } from '#solana/web3.js';
const TOKEN_PROGRAM_ID = new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
const ASSOCIATED_TOKEN_PROGRAM_ID = new PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL');
const owner = new PublicKey('CGP6sKHyrZGPJRoUAy8XbyzmX7YD4tVBQG9SEe9ekZM6');
const mint = new PublicKey('Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr');
const [address] = await PublicKey.findProgramAddress(
[owner.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()],
ASSOCIATED_TOKEN_PROGRAM_ID
);
The real code is at: https://github.com/solana-labs/solana-program-library/blob/5611ad8bd595d9e3666f8b115cd28f8116038645/token/js/src/state/mint.ts#L146
const connection = new Connection("https://api.devnet.solana.com");
let response = await connection.getTokenAccountsByOwner(
new PublicKey("27kVX7JpPZ1bsrSckbR76mV6GeRqtrjoddubfg2zBpHZ"), // owner here
{
programId: TOKEN_PROGRAM_ID,
}
);
This is to get all token account owned by specific wallet address. Dont forget to adjust connection url if you are using mainnet or testnet or localhost. You also can filter it to a specific mint address as well.
I'm using Next.js, web3-react, ethers.js, MetaMask and Ganache to run a local blockchain.
What I'm trying to do: when the user clicks a button on the front-end, that button opens MetaMask where some tokens (I "minted/created" these tokens) are sent from the smart contract/smart contract creator to the user upon user consent.
The problem is: when the user clicks the button, the MetaMask pop-up appears, but the sender/recipient addresses are the same.
What I want is for the sender address to be the same as the smart contract/smart contract creator and this transaction should be done by MetaMask.
The following code is what I've done so far:
import { ethers, Contract } from 'ethers'
import { useWeb3React } from '#web3-react/core'
import HRC20 from '../../assets/HRC20.json'
const { account: userAccountAddress, library } = useWeb3React()
const provider = new ethers.providers.Web3Provider(
library.provider // this is the same as `window.ethereum.provider` injected by MetaMask
)
const signer = provider.getSigner()
const tokenContract = new ethers.Contract(
'0x4feEc53a54e36C80A2F0a47454Ab285B99A1a240',
HRC20.abi,
provider
)
const tokenContractWithSigner = contract.connect(signer)
const tx = tokenContractWithSigner.transfer(
userAccountAddress,
10000000000
)
My guess is that I need to specify the sender address when creating the provider or signer or something.
Based on calling tokenContractWithSigner.transfer, you have 2 args
function transfer(address _to, uint _value) public returns (bool success){
// make sure callee has enough balance
require(balanceOf[msg.sender]>=_value);
// ... more logic
to=payable(_to)
to.transfer(value);
}
Or:
function transfer(address _to, uint _value) public returns (bool success){
// make sure callee has enough balance
require(balanceOf[msg.sender]>=_value);
// ... more logic
to=payable(_to)
(bool success,)=to.call{value:_value}("");
require(success,"Transfer failed!");
}
When you call this:
tokenContractWithSigner.transfer(
userAccountAddress,
10000000000
).send({from:getAccount})
According to the ERC20 standart, the transfer method of an ERC20 contract is used to transfer tokens from the sender of the transaction, to the address specified. Because it is the same address that is sent the transaction and is given to the transfer method, the contract thinks you want to transfer tokens from an address to the same address.
To mint new tokens you need to add a mint method to your ERC20 contract.
I did find a solution to my problem: first, I can only make a transaction using MetaMask when the sender is me. I cannot send tokens on behalf of another wallet, even if I own it.
My approach was to create an endpoint on the backend that would receive the user's wallet address; then I connect to the wallet that holds the funds (usually the wallet that deployed the token) using its private key and that wallet signs the transaction. On the front end, I listen for a Transfer event so I can notify the user when their transactions are mined/confirmed.
Reference: https://ethereum.org/en/developers/tutorials/send-token-etherjs/#send-token-method
I am new to azure, tokens and so on...
I have "digged" microsoft documentation and google and stackoverflow, but still didn't get full understanding.
So I using openId with Owin library to connect to azure from web app(VS2013 .net 4.5.1). And I have next code to do it:
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(
CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
MetadataAddress = String.Format(aadInstance, tenant, policy),
AuthenticationType = policy,
ClientId = clientId,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = AuthenticationFailed
,SecurityTokenValidated = OnSecurityTokenValidated
,AuthorizationCodeReceived = OnAuthorizationCodeReceived
,SecurityTokenReceived = OnSecurityTokenReceived
},
Scope = "openid profile",
ResponseType = "id_token"
};
);
}
private Task OnSecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
var identity = notification.AuthenticationTicket.Identity;
var claims = notification.OwinContext.Authentication.User.Claims;
ClaimsPrincipal.Current.AddIdentity(identity);
return Task.FromResult(0);
}
And it is working, but in microsoft documentation I found next instruction "Currently, ID tokens are signed but not encrypted. When your app receives an ID token, it must validate the signature to prove the token's authenticity and validate a few claims in the token to prove its validity. The claims validated by an app vary depending on scenario requirements, but your app must perform some common claim validations in every scenario."
But there is SecurityTokenValidated-callback , which have AuthenticationTicket. So do I still need to somehow validate token/ticked or now it is handled automatically (I been tough in army that nothing happening automatically, but still)?
The library that you are using handles the validation for you.
It will check the signature is what it should be based on the keys provided by Azure AD.
So you don't need to do manual checks, other than your app's specific checks. For example, an app might allow only members of a certain group to access the app. You would need to do that check if that is the case.
OK I am now days into this and have made significant progress but am still completely stumped about the fundamentals.
My application uses Cognito User Pools for creating and managing users - these are identified on S3 it seems by their IdentityId. Each of my users has their own S3 folder, and AWS automatically gives them a folder name that is equal to the user's IdentityId.
I need to relate the IdentityId to the other Cognito user information but cannot work out how.
The key thing I need is to be able to identify the username plus other cognito user attributes for a given IdentityId - and it's insanely hard.
So the first battle was to work out how to get the IdentityId when a Cognito user does a request via the AWS API Gateway. Finally I got that worked out, and now I have a Cognito user, who does a request to the API Gateway, and my Lambda function behind that now has the IdentityId. That bit works.
But I am completely stumped as to how to now access the Cognito user's information that is stored in the user pool. I can't find any clear information, and certainly no code, that shows how to use the IdentityId to get the Cognito user's attributes, username etc.
It appears that if I use a "Cognito user pool" to authorize my method in API Gateway, then the body mapping template can be used to put Cognito User information such as the sub and the username and email address into the context, BUT I do NOT get the IdentityId.
BUT if I use the AWS_IAM to authorize my method in the API gateway then the body mapping template does the inverse - it gives me the IdentityId but not the Cognito user fields such as sub and username and email.
It's driving me crazy - how can I get the IdentityId and all the Cognito users fields and attributes together into one data structure? The fact that I seem to be only able to get one or the other just makes no sense.
It turns out that to get the IdentityId AND user details at the same time using AWS Lambda/Cognito/API Gateway, you need to have a Lambda function that is authenticated using AWS_IAM (NOT COGNITO_USER_POOLS), you must send your request the AWS API Gateway, BUT it MUST be a signed request, you must then modify the integration request body mapping templates so that you are given the IdentityId in the event (maybe the context? can't remember). Now you have the IdentityId. Phew. Now you must submit the client's Cognito ID token from the front end to the back end. It is important to validate the token - you cannot trust that it has not been tampered with if you do not validate it. To decode and validate the token you must get the keys from your userpool, put them into your script, ensure that you have jwt decoding libraries plus signature validation libraries included in your AWS lambda zipfile. Now your script must validate the token submitted from the front end and then you can get the user details out of the token. Voila! Now you have both IdentityId plus user details such as their sub, username and email address. So easy.
The above is what is takes to get the username associated with an IdentityId using AWS Cognito/Lambda/API Gateway. This took me days to get working.
Can I please say to any Amazon employees who wander across this ........ well it's WAY too hard to get the user details associated with an IdentityId. You need to fix this. It made me angry that this was so hard and burned so much of my time.
The solution:
I did this by modifying an Amazon employees custom authorizer here:
https://s3.amazonaws.com/cup-resources/cup_custom_authorizer_lambda_function_blueprint.zip
as found and described here:
https://aws.amazon.com/blogs/mobile/integrating-amazon-cognito-user-pools-with-api-gateway/
use strict';
let util = require('util');
var jwt = require('jsonwebtoken');
var jwkToPem = require('jwk-to-pem');
var userPoolId = 'YOUR USERPOOL ID';
var region = 'YOUR REGION'; //e.g. us-east-1
var iss = 'https://cognito-idp.' + region + '.amazonaws.com/' + userPoolId;
//https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html
// DOWNLOAD FROM https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json
let userPoolKeys = {PUT YOUR DOWNLOADED USER POOL KEYS JSON HERE};
var pems = {};
let convertKeysToPems = () => {
var keys = userPoolKeys['keys'];
for(var i = 0; i < keys.length; i++) {
//Convert each key to PEM
var key_id = keys[i].kid;
var modulus = keys[i].n;
var exponent = keys[i].e;
var key_type = keys[i].kty;
var jwk = { kty: key_type, n: modulus, e: exponent};
var pem = jwkToPem(jwk);
pems[key_id] = pem;
}
}
exports.handler = function(event, context) {
convertKeysToPems()
console.log(event);
let token = event['body-json'].cognitoUserToken;
console.log(event['body-json'].cognitoUserToken);
ValidateToken(pems, event, context, token);
};
let ValidateToken = (pems, event, context, token) => {
//Fail if the token is not jwt
var decodedJwt = jwt.decode(token, {complete: true});
console.log(decodedJwt)
if (!decodedJwt) {
console.log("Not a valid JWT token");
context.fail("Unauthorized");
return;
}
//Fail if token is not from your UserPool
if (decodedJwt.payload.iss != iss) {
console.log("invalid issuer");
context.fail("Unauthorized");
return;
}
//Reject the jwt if it's not an 'Access Token'
if (decodedJwt.payload.token_use != 'id') {
console.log("Not an id token");
context.fail("Unauthorized");
return;
}
//Get the kid from the token and retrieve corresponding PEM
var kid = decodedJwt.header.kid;
var pem = pems[kid];
if (!pem) {
console.log(pems, 'pems');
console.log(kid, 'kid');
console.log('Invalid token');
context.fail("Unauthorized");
return;
}
//Verify the signature of the JWT token to ensure it's really coming from your User Pool
jwt.verify(token, pem, { issuer: iss }, function(err, payload) {
if(err) {
context.fail("Unauthorized");
} else {
let x = decodedJwt.payload
x.identityId = context.identity.cognitoIdentityId
//let x = {'identityId': context['cognito-identity-id'], 'decodedJwt': decodedJwt}
console.log(x);
context.succeed(x);
}
});
}
This problem -- the problem of using the user's sub instead of their identityId in S3 paths and how to set that up -- is 100% solved for me thanks to the help of #JesseDavda's solution to this problem in this issue: https://github.com/aws-amplify/amplify-js/issues/54
For all of you developers who have been trying to get the identityId in lambdas so that your Amplify default paths in S3 work - this solution simply ends up ignoring identityId altogether - it is a solution that sets up the paths in S3 based on sub instead of the identityId. At the end of this solution, you will never have to deal with more than one id for your users, you will never have to deal with identityId (hopefully) ever again.
If I'm understanding this correctly you want the CognitoIdentityId and the User attributes in the same place. How we do it is the following way:
From the event request context we get the IdentityId:
event.requestContext.identity.cognitoIdentityId
Also from the request context we get the user's sub:
event.requestContext.identity.cognitoAuthenticationProvider.split(':CognitoSignIn:')[1]
Then with the sub you can request the rest of the attributes the following way:
const AWS = require('aws-sdk');
let cognito = new AWS.CognitoIdentityServiceProvider();
let request = {
Username: userSub,
UserPoolId: process.env.userPoolId,
};
let result = await cognito.adminGetUser(request).promise();
const userAttributes = result.UserAttributes.reduce((acc, attribute) => {
const { Name, Value } = attribute;
acc[Name] = Value;
return acc;
}, {});
return userAttributes;