I am trying to create a mechanism to sell NFTs on a marketplace for Solana. I have created the following function that works correctly, making the swap. However, the royalties in the metadata are not being considered.
let sendNft = async function () {
let connection = new Connection("https://api.mainnet-beta.solana.com" , "confirmed")
let buyerfirstWinPrivKey = key
let buyerKey = Keypair.fromSeed(Uint8Array.from(buyerfirstWinPrivKey));
let ownerfirstWinPrivKey = key
let ownerKey = Keypair.fromSeed(Uint8Array.from(ownerfirstWinPrivKey));
let mintPubkey = new PublicKey(mint_id); //
// calculate ATA
const accountOwner = await getOrCreateAssociatedTokenAccount(
connection,
ownerKey, //
mintPubkey, // mint
ownerKey.publicKey // owner
);
// calculate ATA
const accountBuyer = await getOrCreateAssociatedTokenAccount(
connection,
ownerKey,
mintPubkey, // mint
buyerKey.publicKey // buyer
);
const transaction = new Transaction().add(SystemProgram.transfer({
fromPubkey: buyerKey.publicKey,
toPubkey: ownerKey.publicKey,
lamports: 100000
}));
// Transfer NFT
transaction.add(
createTransferCheckedInstruction(
accountOwner.address, // from (should be a token account)
mintPubkey, // mint
accountBuyer.address, // to (should be a token account)
ownerKey.publicKey,
1, // Number of tokens
0, // Decimals
[], // for multisig account, leave empty.
TOKEN_PROGRAM_ID
)
);
console.log(`txhash: ${await connection.sendTransaction(transaction, [buyerKey, ownerKey])}`);
};
When I check the transaction I get the following data:
Transaction
When I make the sale via marketplace I get the following data, which considers the royalties:
Sale via marketplace
How can I make the swap considering the royalties ?
Related
Let's say I create an account foo.near on NEAR with a single full access public key ed25519:publiccc which is matched to a private key ed25519:secrettt and stored locally in the .near-credentials/mainnet/foo.near.json file.
I can now send myself 1 NEAR using near send foo.near foo.near 1 to verify the keys work. If I modify the private key in that keypair at all, the send fails. Good.
Then I replace the public key in that keypair with a nonsense string like ed25519:giggle.
I can still send myself 1 NEAR using the same command. Why?
It appears the public key is totally irrelevant in this keypair. You can use a nonsense string or a public key that's supposed to have limited access only and the private key gives full access to transfer funds.
Good question! I had to do some digging. So let's start with what the CLI is doing behind the scenes. The code is found here:
exports.sendMoney = async function (options) {
await checkCredentials(options.sender, options.networkId, options.keyStore);
console.log(`Sending ${options.amount} NEAR to ${options.receiver} from ${options.sender}`);
const near = await connect(options);
const account = await near.account(options.sender);
const result = await account.sendMoney(options.receiver, utils.format.parseNearAmount(options.amount));
inspectResponse.prettyPrintResponse(result, options);
};
Essentially it connects to the network, creates an account object, and then calls the sendMoney method for that account object which is through near-api-js. The way it's instantiated is through near.account() which behind the scenes does the following (found here):
async account(accountId: string): Promise<Account> {
const account = new Account(this.connection, accountId);
return account;
}
This is just calling the class' constructor passing in the connection and account ID. Up until this point, we have no reference to the private or public key other than the connection which has a keystore indicating WHERE the keypair will be found. Ok moving on.
After the account class is initialized, the sendMoney method is called and the only action is a simple transfer with the amount. The code for this method can be seen here.
async sendMoney(receiverId: string, amount: BN): Promise<FinalExecutionOutcome> {
return this.signAndSendTransaction({
receiverId,
actions: [transfer(amount)]
});
}
You'll notice that it calls the signAndSendTransaction method that is part of the Account Class (found here).
async signAndSendTransaction({ receiverId, actions, returnError }: SignAndSendTransactionOptions): Promise<FinalExecutionOutcome> {
let txHash, signedTx;
// TODO: TX_NONCE (different constants for different uses of exponentialBackoff?)
const result = await exponentialBackoff(TX_NONCE_RETRY_WAIT, TX_NONCE_RETRY_NUMBER, TX_NONCE_RETRY_WAIT_BACKOFF, async () => {
[txHash, signedTx] = await this.signTransaction(receiverId, actions);
const publicKey = signedTx.transaction.publicKey;
try {
return await this.connection.provider.sendTransaction(signedTx);
}
...
...
Out of this method, the important thing is this.signTransaction whose method is found here:
protected async signTransaction(receiverId: string, actions: Action[]): Promise<[Uint8Array, SignedTransaction]> {
const accessKeyInfo = await this.findAccessKey(receiverId, actions);
if (!accessKeyInfo) {
throw new TypedError(`Can not sign transactions for account ${this.accountId} on network ${this.connection.networkId}, no matching key pair exists for this account`, 'KeyNotFound');
}
const { accessKey } = accessKeyInfo;
const block = await this.connection.provider.block({ finality: 'final' });
const blockHash = block.header.hash;
const nonce = accessKey.nonce.add(new BN(1));
return await signTransaction(
receiverId, nonce, actions, baseDecode(blockHash), this.connection.signer, this.accountId, this.connection.networkId
);
}
In this method, there's a sneaky this.findAccessKey being done whose code can be found here.
async findAccessKey(receiverId: string, actions: Action[]): Promise<{ publicKey: PublicKey; accessKey: AccessKeyView }> {
// TODO: Find matching access key based on transaction (i.e. receiverId and actions)
const publicKey = await this.connection.signer.getPublicKey(this.accountId, this.connection.networkId);
if (!publicKey) {
throw new TypedError(`no matching key pair found in ${this.connection.signer}`, 'PublicKeyNotFound');
}
const cachedAccessKey = this.accessKeyByPublicKeyCache[publicKey.toString()];
if (cachedAccessKey !== undefined) {
return { publicKey, accessKey: cachedAccessKey };
}
try {
const rawAccessKey = await this.connection.provider.query<AccessKeyViewRaw>({
request_type: 'view_access_key',
account_id: this.accountId,
public_key: publicKey.toString(),
finality: 'optimistic'
});
// store nonce as BN to preserve precision on big number
const accessKey = {
...rawAccessKey,
nonce: new BN(rawAccessKey.nonce),
};
// this function can be called multiple times and retrieve the same access key
// this checks to see if the access key was already retrieved and cached while
// the above network call was in flight. To keep nonce values in line, we return
// the cached access key.
if (this.accessKeyByPublicKeyCache[publicKey.toString()]) {
return { publicKey, accessKey: this.accessKeyByPublicKeyCache[publicKey.toString()] };
}
this.accessKeyByPublicKeyCache[publicKey.toString()] = accessKey;
return { publicKey, accessKey };
} catch (e) {
if (e.type == 'AccessKeyDoesNotExist') {
return null;
}
throw e;
}
}
If you take a look at this code, you'll notice a really important line:
const publicKey = await this.connection.signer.getPublicKey(this.accountId, this.connection.networkId);
What is this returning? The short answer is that if you're using the NEAR-CLI, the connection.signer comes from UnencryptedFileSystemKeyStore since your keys are stored in .near-credentials. Now let's look at this getPublicKey method found here:
async getPublicKey(accountId?: string, networkId?: string): Promise<PublicKey> {
const keyPair = await this.keyStore.getKey(networkId, accountId);
if (keyPair === null) {
return null;
}
return keyPair.getPublicKey();
}
It does this.keyStore.getKey where the keyStore is the UnencryptedFileSystemKeyStore I mentioned earlier. The getKey method is then found here:
async getKey(networkId: string, accountId: string): Promise<KeyPair> {
// Find key / account id.
if (!await exists(this.getKeyFilePath(networkId, accountId))) {
return null;
}
const accountKeyPair = await readKeyFile(this.getKeyFilePath(networkId, accountId));
return accountKeyPair[1];
}
In this function, the final culprit is the readKeyFile where it returns the key pair (both public and private). This can be found here:
export async function readKeyFile(filename: string): Promise<[string, KeyPair]> {
const accountInfo = await loadJsonFile(filename);
// The private key might be in private_key or secret_key field.
let privateKey = accountInfo.private_key;
if (!privateKey && accountInfo.secret_key) {
privateKey = accountInfo.secret_key;
}
return [accountInfo.account_id, KeyPair.fromString(privateKey)];
}
Notice that it returns a KeyPair that is generated from the private key. This means that no matter what the public key is, it will overwrite it. I did a quick test where I console logged the accountInfo and it indeed returned the incorrect public key that I had set manually but when it generates the KeyPair from the private key, that information is overwritten.
TLDR:
When the your account signs a transaction, the public key is re-generated based on the private key. It doesn't matter what public key you have in your .near-credentials since it is overwritten when the KeyPair is created based on the private key.
I am having trouble getting serverside near-api-js account creation and contract deployment working successfully and would appreciate some input.
I am using a unencrypted local keystore set up as follows;
const privatekey = "__private key__";
const keyPair = KeyPair.fromString(privatekey); // generate key pair
const keyStore = new nearAPI.keyStores.UnencryptedFileSystemKeyStore(credentialsPath); // declare keystore type
const config = Object.assign(require('./config')('testnet'), {
networkId: networkId,
deps: { keyStore },
}); // setup config for near connection with keystore added
// update keystore with main account
await keyStore.setKey('testnet', config.masterAccount, nearAPI.utils.KeyPair.fromString(privatekey));
// connect near with config details
const near = await nearAPI.connect(config);
// load master account
const account = await new nearAPI.Account(near.connection, config.masterAccount);
I am creating a subaccount as;
const amountInYocto = utils.format.parseNearAmount("5");
// generate new public key
const newPublicKey = await near.connection.signer.createKey(newAccountName,config.networkId);
let keyPair;
let publicKey;
keyPair = await KeyPair.fromRandom('ed25519');
publicKey = keyPair.getPublicKey();
await near.connection.signer.keyStore.setKey(near.connection.networkId, newAccountName, keyPair);
const response = await near.createAccount(newAccountName, publicKey, amountInYocto);
When I try to deploy a contract to the created account I get the error;
Actor 'master account' doesn't have permission to account 'subaccount' to complete the action
This is how I deploy a contract:
const txs = [transactions.deployContract(fs.readFileSync(`../near/out/out.wasm`))];
const result = await account.signAndSendTransaction({
receiverId: subaccount,
actions: txs
});
Im sure I have something wrong in how i manage the keys, but cannot work it out. Any suggestions?
const connection = new anchor.web3.Connection("https://api.devnet.solana.com");
const provider = new anchor.Provider(connection, wallet, {
preflightCommitment: "recent",
});
const programid = new anchor.web3.PublicKey("EcgzWq52GhdCRJFEXub5LCgdqPs5Eid2XfiZSXM22eQu");
const res = await axios.get(process.env.REACT_APP_API_ENDPOINT+"/stakeidl");
const idl = await res.data;
const program = new anchor.Program(idl, programid, provider);
console.log(program);
const [collections,bump] = (await PublicKey.findProgramAddress(["collections"],programid));
console.log(collections,bump);
const tx = await program.rpc.setAdmin(bump,{accounts:
{collections:collections,
admin:wallet.publicKey,
systemProgram:anchor.web3.SystemProgram.programId}});
Above code is throwing custom program error 0xbbf
I create program using publickey and idl and call setAdmin on rust it is giving 0xbbf
The 0xbbf is the hex representation for 3007. You can see a description of all errors here, for example. https://solanacookbook.com/references/anchor.html#error-codes
I guess in this case, some of the seeds for a PDA don't match up with the right owner, or maybe deployed by a wrong program.
I'm trying to test the Chainlink alarm oracle following the documentation steps with hardhat on a kovan fork network.
https://docs.chain.link/docs/chainlink-alarm-clock/#:~:text=ala
When I test the contract with npx hardhat test it looks like it hits the request function to the oracle but never calling back the fulfill_alarm().
I wonder if that might be an issue with the fork network or with my testing code.
I'm using Alchemy api as url for the fork network on kovan.
Also, I'm using a timeout with mocha so it can wait for the callback to execute, but never hits.
The first it statement executes correctly, but then on the second one which is suposed to increment lotteryId + 1 once the lottery is done (fulfill_alarm() called) doesn't pass. It looks like the function never gets executed by the chainlink oracle.
oracle: 0xAA1DC356dc4B18f30C347798FD5379F3D77ABC5b
jobId: '982105d690504c5d9ce374d040c08654'
Solidity code:
//Starting Oracle Alarm
function start_new_lottery(uint256 duration) public {
require(lottery_state == LOTTERY_STATE.CLOSED, "can't start a n
lottery_state = LOTTERY_STATE.OPEN;
Chainlink.Request memory req = buildChainlinkRequest(jobId, add
req.addUint("until", block.timestamp + duration);
sendChainlinkRequestTo(oracle, req, oraclePayment);
console.log('LINK REQ SEND');
}
//Callback Function after Oracle Alarm is Fulfilled
function fulfill_alarm(bytes32 _requestId) public recordChainlinkFu
require(lottery_state == LOTTERY_STATE.OPEN, "The lottery hasn'
lotteryId = lotteryId + 1;
console.log('ALARM_FULFILLED');
pickWinner();
}
test code:
const {expect} = require("chai");
const { time } = require("#openzeppelin/test-helpers");
describe("Lottery Contract", function() {
//Defining global variables for testing.
const price_lottery = ethers.BigNumber.from(1);
let owner;
let Lottery;
let lottery;
before(async function () {
Lottery = await ethers.getContractFactory("Lottery");
[owner] = await ethers.getSigners();
lottery = await Lottery.deploy(
price_lottery,
);
//Starting new Lottery
await lottery.start_new_lottery(60);
});
it("should start a chainlink alarm to init new lottery", async func
let lottery_state = await lottery.lottery_state();
expect(lottery_state).to.equal(0);
//Initial value
let lotteryId = await lottery.lotteryId();
expect(lotteryId).to.equal(1);
})
it("increments lotteryID + 1, when chainlink alarm is fulfilled aft
//Wait until alarm is fulfilled
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
await timeout(120000);
//Expected value
let lotteryId = await lottery.lotteryId();
expect(lotteryId).to.equal(2);
})
})
I have this code, and failing to understand why I am not getting inside the map function (where I have the comment "I AM NEVER GETTING TO THIS PART OF THE CODE"):
export const fiveCPMonitoringLoadEpic = (action$, store) =>
action$
.ofType(
FIVE_CP_MONITORING_ACTION_TYPES.LOAD_FIVE_CP_MONITORING_DATA_STARTED
)
.debounceTime(250)
.switchMap(action => {
const params = action.params;
const siteId = { params };
// getting site's EDC accounts (observable):
const siteEdcAccount$ = getSiteEDCAccountsObservable(params);
const result$ = siteEdcAccount$.map(edcResponse => {
// getting here - all good so far.
const edcAccount = edcResponse[0];
// creating another observable (from promise - nothing special)
const fiveCPMonitoringEvent$ = getFiveCPAndTransmissionEventsObservable(
{
...params,
edcAccountId: edcAccount.utilityAccountNumber
}
);
fiveCPMonitoringEvent$.subscribe(x => {
// this is working... I am getting to this part of the code
// --------------------------------------------------------
console.log(x);
console.log('I am getting this printed out as expected');
});
return fiveCPMonitoringEvent$.map(events => {
// I NEVER GET TO THIS PART!!!!!
// -----------------------------
console.log('----- forecast-----');
// according to response - request the prediction (from the event start time if ACTIVE event exists, or from current time if no active event)
const activeEvent = DrEventUtils.getActiveEvent(events);
if (activeEvent) {
// get event start time
const startTime = activeEvent.startTime;
// return getPredictionMeasurementsObservable({...params, startTime}
const predictions = getPredictionMock(startTime - 300);
return Observable.of(predictions).delay(Math.random() * 2000);
} else {
// return getPredictionMeasurementsObservable({...params}
const predictions = getPredictionMock(
DateUtils.getLocalDateInUtcSeconds(new Date().getTime())
);
return Observable.of(predictions).delay(Math.random() * 2000);
}
});
can someone please shed some light here?
why when using subscribe it is working, but when using map on the observable it is not?
isn't map suppose to be invoked every time the observable fires?
Thanks,
Jim.
Until you subscribe to your observable, it is cold and does not emit values. Once you subscribe to it, the map will be invoked. This is a feature of rxjs meant to avoid operations that make no change (= no cunsumer uses the values). There are numerous blog posts on the subject, search 'cold vs hot obserables' on google