1) You need a core + horizon server to have access to the stellar network but for the beginning you can simply start with the public ones provided by SDF (Stellar Development Foundation).
2) Pick a sdk for the language of your choice, it's api will give you all methods you need to query stellar accounts and create transactions.
3) Learn what you need to know about stellar transactions. In a nutshell: A transaction has a source account which is going to take the network transaction fees. It wraps one or more operations like payments or changing trustlines. By default operations apply to the transactions source account but you can define a different source account for each operation as well. This means you can complete your use case in a single transaction:
- Generate a keypair for your new user
- Create transaction from a well funded account
- Add Operation: Create user account with starting balance of 1.5 XLM
- Add Operation: Create a trustline from user to your asset
- Add Operation: Send asset tokens to user
- Sign tx with your own keypair (keypairs, if different accounts involved)
- Sign tx with the new users keypair
- Submit it to the network.
An easy way to get used to transactions is playing around with Stellar Laboratory Transaction Builder
4) Implement it, here is an example in javascript:
const StellarSdk = require('stellar-sdk');
(async () => {
try {
StellarSdk.Network.useTestNetwork();
const horizon = new StellarSdk.Server('https://horizon-testnet.stellar.org');
// Generate keypair for a new account
// You might want to do this on the client side so new users
// would not expose their secret keys
const newAccountKeypair = StellarSdk.Keypair.random();
// This account creates the tx and funds the new account
const txCreatorKeypair = StellarSdk.Keypair.fromSecret(
'SBUBZYQPOYPCKCMGEZ6OPFIJSL4DCOK6VDP6I2SWF7VKFRHASKLVPDV4'
);
// This is the asset issuers account
const assetIssuerKeypair = StellarSdk.Keypair.fromSecret(
'SDSAMCM7PVBMLTRINCTCXOKHK2V7H42OFWDMAD7GUEM2P4FWTYYBBJHX'
);
// Our custom asset is defined by its asset code and the issuers accountID
const asset = new StellarSdk.Asset(
'GUINEAPIGS',
assetIssuerKeypair.publicKey()
);
// Load account, cause tx needs its sequence number + 1
const account = await horizon.loadAccount(txCreatorKeypair.publicKey());
// Build a transaction...
const transaction = await new StellarSdk.TransactionBuilder(account)
.setTimeout(5000)
// ... with a createAccount operation for the new acc
.addOperation(StellarSdk.Operation.createAccount({
startingBalance: '1.5',
destination: newAccountKeypair.publicKey()
}))
// ... create a trustline
.addOperation(StellarSdk.Operation.changeTrust({
asset: asset,
source: newAccountKeypair.publicKey()
}))
// ... send 100 fresh minted tokens from issuer
.addOperation(StellarSdk.Operation.payment({
asset: asset,
destination: newAccountKeypair.publicKey(),
amount: '100',
source: assetIssuerKeypair.publicKey(),
}))
.build();
// Needs to sign because its creating tx and funding XLM
transaction.sign(txCreatorKeypair);
// Needs to sign because funding custom asset
transaction.sign(assetIssuerKeypair);
console.log("==========================");
console.log('This is the transaction signed by creator + issuer: ' + transaction.toEnvelope().toXDR('base64') );
console.log("==========================");
// Needs to sign because it gets a trustline created
// Needs to be done on client side if secret is not shared.
transaction.sign(newAccountKeypair);
console.log("Submitting to network...");
const result = await horizon.submitTransaction(transaction);
console.log("Transaction successfully sent and recorded in ledger " + result.ledger);
console.log("txhash = " + result.hash);
console.log("New account public: " + newAccountKeypair.publicKey() );
console.log("New account secret: " + newAccountKeypair.secret() );
} catch (e) {
console.log(e);
}
})();