Hello, World in Solana with Rust & Web3.js

My friend Sameer and I are currently working on our first Web3 project together, and we’re trying to learn how to use Solana. So far it’s been kind of complicated and a bit of a pain, so I just wanted to jot down a few notes here in this blog post for future reference. Mainly, I want to record my notes on getting to “hello, world” in Solana using Rust & Web3.js.

So far I haven’t found a great tutorial yet, however, there’s an okay course called recommended by the Solana team called Solana 101 that my friend and I both just took, and I think it has most of the basic building blocks you’ll need to get started with Solana programming.

If you’d like to take the Solana 101 course yourself, you can do that here: Solana 101

I believe the course used to be something you could take on Figment’s website, but now it’s simply a Github repo that you download. After you download the repo you an follow the instructions in the README to launch the course in your web browser running off of a local web server running on your machine.

Now for the real meat of what I wanted to post here… The course above has like 12 different lessons all featuring a code snippet demonstrating how to do some basic operation in Solana. However, the course isn’t hosted on the internet anymore! If you want to reference it or take it again, you have to spin your local web server up again to read the code snippets. So I decided that I really wanted to have a copy of all the code snippets up on the web that I could reference when needed. So without further ado, the 12 essential code snippets from Figment’s Solana 101 Course:

Solana 101: Connect to Solana (From Figment.io’s Solana 101 Course)

import type {NextApiRequest, NextApiResponse} from 'next';
import {getNodeURL} from '@figment-solana/lib';
import {Connection} from '@solana/web3.js';
export default async function connect(
req: NextApiRequest,
res: NextApiResponse<string>,
) {
try {
const {network} = req.body;
const url = getNodeURL(network);
const connection = new Connection(url, 'confirmed');
const version = await connection.getVersion();
res.status(200).json(version['solana-core']);
} catch (error) {
let errorMessage = error instanceof Error ? error.message : 'Unknown Error';
res.status(500).json(errorMessage);
}
}
view raw connect.ts hosted with ❤ by GitHub

Solana 101: Creating Solana Accounts AKA Keypair (From Figment.io’s Solana 101 Course)

import type {NextApiRequest, NextApiResponse} from 'next';
import {Keypair} from '@solana/web3.js';
/*
* Like with most Web 3 protocols, transactions on Solana happen between accounts.
* To create an account, a client generates a keypair which has a public key (or
* address, used to identify and lookup an account) and a secret key used to sign
* transactions.
*/
type ResponseT = {
secret: string;
address: string;
};
export default function keypair(
_req: NextApiRequest,
res: NextApiResponse<string | ResponseT>,
) {
try {
const keypair = Keypair.generate();
const address = keypair?.publicKey.toString();
const secret = JSON.stringify(Array.from(keypair.secretKey));
res.status(200).json({
secret,
address,
});
} catch (error) {
let errorMessage = error instanceof Error ? error.message : 'Unknown Error';
res.status(500).json(errorMessage);
}
}
view raw keypair.ts hosted with ❤ by GitHub

Solana 101: Funding Your Solana Account via Airdrop (From Figment.io’s Solana 101 Course)

import {Connection, PublicKey, LAMPORTS_PER_SOL} from '@solana/web3.js';
import type {NextApiRequest, NextApiResponse} from 'next';
import {getNodeURL} from '@figment-solana/lib';
export default async function fund(
req: NextApiRequest,
res: NextApiResponse<string>,
) {
try {
const {network, address} = req.body;
const url = getNodeURL(network);
const connection = new Connection(url, 'confirmed');
const publicKey = new PublicKey(address);
const hash = await connection.requestAirdrop(publicKey, LAMPORTS_PER_SOL);
await undefined;
res.status(200).json(hash);
} catch (error) {
let errorMessage = error instanceof Error ? error.message : 'Unknown Error';
res.status(500).json(errorMessage);
}
}
view raw fund.ts hosted with ❤ by GitHub

Solana 101: Getting Your Balance (From Figment.io’s Solana 101 Course)

import type {NextApiRequest, NextApiResponse} from 'next';
import {Connection, PublicKey} from '@solana/web3.js';
import {getNodeURL} from '@figment-solana/lib';
export default async function balance(
req: NextApiRequest,
res: NextApiResponse<string | number>,
) {
try {
const {network, address} = req.body;
const url = getNodeURL(network);
const connection = new Connection(url, 'confirmed');
const publicKey = new PublicKey(address);
const balance = await connection.getBalance(publicKey);
if (balance === 0 || balance === undefined) {
throw new Error('Account not funded');
}
res.status(200).json(balance);
} catch (error) {
let errorMessage = error instanceof Error ? error.message : 'Unknown Error';
res.status(500).json(errorMessage);
}
}
view raw balance.ts hosted with ❤ by GitHub

Solana 101: Transfering Funds (From Figment.io’s Solana 101 Course)

import type {NextApiRequest, NextApiResponse} from 'next';
import {getNodeURL} from '@figment-solana/lib';
import {
Connection,
PublicKey,
SystemProgram,
Transaction,
sendAndConfirmTransaction,
} from '@solana/web3.js';
export default async function transfer(
req: NextApiRequest,
res: NextApiResponse<string>,
) {
try {
const {address, secret, recipient, lamports, network} = req.body;
const url = getNodeURL(network);
const connection = new Connection(url, 'confirmed');
const fromPubkey = new PublicKey(address);
const toPubkey = new PublicKey(recipient);
// The secret key is stored in our state as a stringified array
const secretKey = Uint8Array.from(JSON.parse(secret as string));
//... let's skip the beginning as it should be familiar for you by now!
// Find the parameter to pass
const instructions = SystemProgram.transfer({
fromPubkey,
toPubkey,
lamports,
});
// How could you construct a signer array's
const signers = [
{
publicKey: fromPubkey,
secretKey,
},
];
// Maybe adding something to a Transaction could be interesting ?
const transaction = new Transaction().add(instructions);
// We can send and confirm a transaction in one row.
const hash = await sendAndConfirmTransaction(connection, transaction, signers);
res.status(200).json(hash);
} catch (error) {
let errorMessage = error instanceof Error ? error.message : 'Unknown Error';
res.status(500).json(errorMessage);
}
}
view raw transfer.ts hosted with ❤ by GitHub

Solana 101: Deploy Rust Program to Solana Blockchain (from Figment.io’s Solana 101 Course)

use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint,
entrypoint::ProgramResult,
msg,
program_error::ProgramError,
pubkey::Pubkey,
};
/// Define the type of state stored in accounts
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct GreetingAccount {
/// number of greetings
pub counter: u32,
}
// Declare and export the program's entrypoint
entrypoint!(process_instruction);
// Program entrypoint's implementation
pub fn process_instruction(
program_id: &Pubkey, // Public key of the account the hello world program was loaded into
accounts: &[AccountInfo], // The account to say hello to
_instruction_data: &[u8], // Ignored, all helloworld instructions are hellos
) -> ProgramResult {
msg!("Hello World Rust program entrypoint");
// Iterating accounts is safer then indexing
let accounts_iter = &mut accounts.iter();
// Get the account to say hello to
let account = next_account_info(accounts_iter)?;
// The account must be owned by the program in order to modify its data
if account.owner != program_id {
msg!("Greeted account does not have the correct program id");
return Err(ProgramError::IncorrectProgramId);
}
// Increment and store the number of times the account has been greeted
let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
greeting_account.counter += 1;
greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;
msg!("Greeted {} time(s)!", greeting_account.counter);
Ok(())
}
// Sanity tests
#[cfg(test)]
mod test {
use super::*;
use solana_program::clock::Epoch;
use std::mem;
#[test]
fn test_sanity() {
let program_id = Pubkey::default();
let key = Pubkey::default();
let mut lamports = 0;
let mut data = vec![0; mem::size_of::<u32>()];
let owner = Pubkey::default();
let account = AccountInfo::new(
&key,
false,
true,
&mut lamports,
&mut data,
&owner,
false,
Epoch::default(),
);
let instruction_data: Vec<u8> = Vec::new();
let accounts = vec![account];
assert_eq!(
GreetingAccount::try_from_slice(&accounts[0].data.borrow())
.unwrap()
.counter,
0
);
process_instruction(&program_id, &accounts, &instruction_data).unwrap();
assert_eq!(
GreetingAccount::try_from_slice(&accounts[0].data.borrow())
.unwrap()
.counter,
1
);
process_instruction(&program_id, &accounts, &instruction_data).unwrap();
assert_eq!(
GreetingAccount::try_from_slice(&accounts[0].data.borrow())
.unwrap()
.counter,
2
);
}
}
view raw lib.rs hosted with ❤ by GitHub

$ yarn run solana:build:program

$ solana program deploy /home/zu/project/figment/learn-web3-dapp/dist/solana/program/helloworld.so

Solana 101: Check Deployed Solana Program from Client (From Figment.io’s Solana 101 Course)

import type {NextApiRequest, NextApiResponse} from 'next';
import {Connection, PublicKey} from '@solana/web3.js';
import {getNodeURL} from '@figment-solana/lib';
import path from 'path';
import fs from 'mz/fs';
const PROGRAM_PATH = path.resolve('dist/solana/program');
const PROGRAM_SO_PATH = path.join(PROGRAM_PATH, 'helloworld.so');
export default async function deploy(
req: NextApiRequest,
res: NextApiResponse<string | boolean>,
) {
try {
const {network, programId} = req.body;
const url = getNodeURL(network);
const connection = new Connection(url, 'confirmed');
// Re-create publicKeys from params
const publicKey = new PublicKey(programId);
const programInfo = await connection.getAccountInfo(publicKey);
if (programInfo === null) {
if (fs.existsSync(PROGRAM_SO_PATH)) {
throw new Error(
'Program needs to be deployed with `solana program deploy`',
);
} else {
throw new Error('Program needs to be built and deployed');
}
} else if (!programInfo.executable) {
throw new Error(`Program is not executable`);
}
res.status(200).json(true);
} catch (error) {
let errorMessage = error instanceof Error ? error.message : 'Unknown Error';
res.status(500).json(errorMessage);
}
}
view raw deploy.ts hosted with ❤ by GitHub

Solana 101: Storing Data on the Solana Blockchain (From Figment.io’s Solana 101 Course)

import {
Connection,
PublicKey,
Keypair,
SystemProgram,
Transaction,
sendAndConfirmTransaction,
} from '@solana/web3.js';
import type {NextApiRequest, NextApiResponse} from 'next';
import {getNodeURL} from '@figment-solana/lib';
import * as borsh from 'borsh';
// The state of a greeting account managed by the hello world program
class GreetingAccount {
counter = 0;
constructor(fields: {counter: number} | undefined = undefined) {
if (fields) {
this.counter = fields.counter;
}
}
}
// Borsh schema definition for greeting accounts
const GreetingSchema = new Map([
[GreetingAccount, {kind: 'struct', fields: [['counter', 'u32']]}],
]);
// The expected size of each greeting account.
const GREETING_SIZE = borsh.serialize(
GreetingSchema,
new GreetingAccount(),
).length;
type ResponseT = {
hash: string;
greeter: string;
};
export default async function greeter(
req: NextApiRequest,
res: NextApiResponse<string | ResponseT>,
) {
try {
const {network, secret, programId: programAddress} = req.body;
const url = getNodeURL(network);
const connection = new Connection(url, 'confirmed');
const programId = new PublicKey(programAddress);
const payer = Keypair.fromSecretKey(new Uint8Array(JSON.parse(secret)));
const GREETING_SEED = 'hello';
// Are there any methods from PublicKey to derive a public key from a seed?
const greetedPubkey = await PublicKey.createWithSeed(
payer.publicKey,
GREETING_SEED,
programId,
);
// This function calculates the fees we have to pay to keep the newly
// created account alive on the blockchain. We're naming it lamports because
// that is the denomination of the amount being returned by the function.
const lamports = await connection.getMinimumBalanceForRentExemption(
GREETING_SIZE,
);
// Find which instructions are expected and complete SystemProgram with
// the required arguments.
const transaction = new Transaction().add(SystemProgram.createAccountWithSeed({
fromPubkey: payer.publicKey,
basePubkey: payer.publicKey,
seed: GREETING_SEED,
newAccountPubkey: greetedPubkey,
lamports,
space: GREETING_SIZE,
programId,
}));
// Complete this function call with the expected arguments.
const hash = await sendAndConfirmTransaction(connection, transaction, [payer]);
res.status(200).json({
hash: hash,
greeter: greetedPubkey.toBase58(),
});
} catch (error) {
let errorMessage = error instanceof Error ? error.message : 'Unknown Error';
res.status(500).json(errorMessage);
}
}
view raw greeter.ts hosted with ❤ by GitHub

Solana 101: Get Data from Solana Blockchain (From Figment.io’s Solana 101 Course)

import type {NextApiRequest, NextApiResponse} from 'next';
import {Connection, PublicKey} from '@solana/web3.js';
import {getNodeURL} from '@figment-solana/lib';
import * as borsh from 'borsh';
// The state of a greeting account managed by the hello world program
class GreetingAccount {
counter = 0;
constructor(fields: {counter: number} | undefined = undefined) {
if (fields) {
this.counter = fields.counter;
}
}
}
// Borsh schema definition for greeting accounts
const GreetingSchema = new Map([
[GreetingAccount, {kind: 'struct', fields: [['counter', 'u32']]}],
]);
export default async function getter(
req: NextApiRequest,
res: NextApiResponse<string | number>,
) {
try {
const {network, greeter} = req.body;
const url = getNodeURL(network);
const connection = new Connection(url, 'confirmed');
const greeterPublicKey = new PublicKey(greeter);
const accountInfo = await connection.getAccountInfo(greeterPublicKey);
if (accountInfo === null) {
throw new Error('Error: cannot find the greeted account');
}
// Find the expected parameters.
const greeting = borsh.deserialize(
GreetingSchema,
GreetingAccount,
accountInfo.data,
);
// A little helper
console.log(greeting);
// Pass the counter to the client-side as JSON
res.status(200).json(greeting.counter);
} catch (error) {
let errorMessage = error instanceof Error ? error.message : 'Unknown Error';
console.log(errorMessage);
res.status(500).json(errorMessage);
}
}
view raw getter.ts hosted with ❤ by GitHub

Solana 101: Set Data on Solana Blockchain (From Figment.io’s Solana 101 Course)

import {
Connection,
PublicKey,
Keypair,
TransactionInstruction,
Transaction,
sendAndConfirmTransaction,
} from '@solana/web3.js';
import type {NextApiRequest, NextApiResponse} from 'next';
import {getNodeURL} from '@figment-solana/lib';
export default async function setter(
req: NextApiRequest,
res: NextApiResponse<string>,
) {
try {
const {greeter, secret, programId, network} = req.body;
const url = getNodeURL(network);
const connection = new Connection(url, 'confirmed');
const greeterPublicKey = new PublicKey(greeter);
const programKey = new PublicKey(programId);
const payerSecretKey = new Uint8Array(JSON.parse(secret));
const payerKeypair = Keypair.fromSecretKey(payerSecretKey);
// this your turn to figure out
// how to create this instruction
const instruction = new TransactionInstruction({
keys: [{pubkey: greeterPublicKey, isSigner: false, isWritable: true}],
programId: programKey,
data: Buffer.alloc(0),
});
// this your turn to figure out
// how to create this transaction
const hash = await sendAndConfirmTransaction(
connection,
new Transaction().add(instruction),
[payerKeypair]
);
res.status(200).json(hash);
} catch (error) {
console.error(error);
res.status(500).json('Get balance failed');
}
}
view raw setter.ts hosted with ❤ by GitHub
 

CacheMoney- How to Repair Your Bank Account Connection

Having issues with your bank account connection to the CacheMoney? To reset your connection simply click the profile button icon in the top right hand corner of our mobile application, then from the settings screen click the REMOVE button next to your bank account connection. To reconnect press the LINK BANK ACCOUNT button on the home screen of the CacheMoney Android app.

 

CacheMoney Help Center

Having trouble with your CacheMoney account? You’ve come to the right place! My name is Chris Pedersen, co-founder and developer of CacheMoney Spending Tracker. If you’re having any issues with CacheMoney or our Plaid/OAuth integrations with your bank account please feel free to contact me personally for support.

Support Contact Name: Christopher Pedersen

Support Contact Phone Number: +1 (469) 261-0727

Support Contact Email: chris@topherpedersen.com

 

Tsunami of Money

by Naval Ravikant

How long does it take to make money? Ha! It take decades. It’s going to take longer than you would like, but the best time to start was yesterday; the second best time is now. So you may as well start if it’s something you’re thinking about, it’s fine to start working on it now. It may take awhile for it to pay out, but when it does start paying out, you will essentially have.. It’s not that you will make money, but you will have become the kind of person who makes money. And once you’re the kind of person that makes money, and you have some comfort around that, then you essentially have your freedom. So you’re basically going to learn the skills of how to make money. You are going to become a money making machine. And once you’re a money making machine, the goal is that in 10 to 20 years from now, you will get hit with a tsunami of money.

Citation: How Long Does it Take to Build Wealth

 

vertically align h1 in div

Alright, so you’re trying to vertically align an h1 tag in a div, and CSS is giving you a hard time? Fret not! I’ve got a quick code React.js snippet here with the CSS you need to center your h1 tag in your div:

export default function Home() {
return(
<div
style={{
position: 'absolute',
display: 'flex',
alignItems: 'center',
height: '100%',
width: '100%',
top: 0,
bottom: 0,
margin: 0,
padding: 0,
backgroundColor: 'purple'
}}
>
<h1
style={{
textAlign: 'center',
width: '100%'
}}
>Vertically & Horizontally Centered Text Here</h1>
</div>
);
}
 

make div fill entire page

Welcome to my really quick tutorial on how to make a div fill the entire page using CSS & React.js. A lot of things seem really simple in CSS, but in reality you end up having to add tons of little extra tweaks to your code to get CSS to render what you would expect. For example, to make a <div> fill the entire page you would expect setting height and width to 100% to do the trick but… it doesn’t! Or maybe setting the height to 100vh and width to 100vw should do the trick right? Nope!

Well here’s the answer you’re looking for, you need to set the <div>’s position to absolute, top to 0, bottom to 0, then set the height and width to 100%. And then for good measure, maybe set the margin and padding to 0 as well?

Anyway, without further ado, How to Make a Div Fill the Entire Page in CSS & React.js:

export default function Home() {
return(
<div
style={{
position: 'absolute',
height: '100%',
width: '100%',
top: 0,
bottom: 0,
margin: 0,
padding: 0,
backgroundColor: 'purple'
}}
>
</div>
);
}
 

How to Download a PDF from a WebView in React-Native

If you happened to have stumble upon this blog post in its current form, I apologize. I’m currently working on adding download functionality to a WebView at work so user’s can download a PDF from my company’s app if the WebView url redirects to a PDF. To figure out how to do this, I’m currently creating an example React-Native app which I will post the source code to for this blog-post/tutorial. However, for this example app I am using my personal blog, and this blog post in particular, to include a link to a PDF.

Long story short… My Example app will feature a WebView pointed at this blog post URL. And this blog post will feature a LINK TO A PDF which the user can click on. The goal will be to intercept the PDF URL and download it if the WebView’s URL changes to a new URL featuring a PDF file instead of HTML.

CONCLUSION– CLICK ON THIS LINK TO VIEW THE PDF

 

TypeScript, JSON.parse, & undefined: Failing Gracefully when Working with JSON Data in TypeScript

Today at work I have some JSON data which I need to parse with JSON.parse, and need to make sure that my code doesn’t crash my company’s app if for some reason our API doesn’t serve back what I’m expecting. Most of the company’s code which deals with fetching data from our API has already been written, but for this assignment I’m having to make my own networking request and handle all of this stuff myself.

So that’s what brings me here now writing this blog post! I think I have a fairly nice fail-safe way of dealing with JSON, JSON.parse, and unknown values/properties that I prototyped this afternoon on repl.it. Take a look at my code snippet below if you need a similar solution. Happy hacking!

const json = '{"foo": { "bar": { "baz": {"a": 1, "b": 2, "c": 3}}}}';
const parsedJSON = JSON.parse(json);
/*
* DANGER! This line will run, but is not fail-safe, could crash if foo, bar,
* baz or a is missing or undefined
*/
console.log(parsedJSON.foo.bar.baz.a);
// This line will fail gracefully foo, bar, baz, or b is missing/undefined
console.log(parsedJSON?.foo?.bar?.baz?.b);
// This line fails gracefully
console.log(parsedJSON?.foo?.bar?.baz?.z);
// DANGER! DANGER! This line will crash
// console.log(parsedJSON?.foo?.bar?.baz?.x.y.z);
 

TODO: Setup a WordPress Blog Hosted Under a Route, /blog, on Flask Website

This isn’t going to be much of a blog post, but I just wanted to bookmark this helpful StackOverflow post on how to setup a WordPress blog which is hosted under a route, /blog, on my Flask site: Proxying to another webservice with Flask

The easy way to host a WordPress blog on a website powered by Flask, Node/Express, Ruby on Rails, etc etc is to simply host the WordPress blog on a subdomain such as blog.mysite.com. However, Google treats subdomains as separate websites! So this kind of defeats the purpose of adding a blog to your site to try and boost it’s SEO rankings. So…

I’m going to try following some of the ideas proposed on the link above and see if I can get this to work on my Flask site.

I’ve been running into a lot of strange SEO problem recently. Another issue is ReactJS and SEO. I’d really like to use React on my site, but I don’t think I can without hurting SEO. (I think React does offer a server side rendering option for this, but you have to use NodeJS instead of Flask). Anyway I’m getting kind of sleepy and rambling now. Goodnight.

 

Support for password authentication was removed on August 13, 2021. Please use a personal access token instead.

This is so stupid, and if you just googled “Support for password authentication was removed on August 13, 2021. Please use a personal access token instead.” I know you agree! Github literally just quit working 3 minutes ago because of their idiotic decision to drop support for passwords.

Okay so what now? I actually don’t know, but… I’m going to figure out how to add a personal access token and drop the instructions here in this blog post:

Step 1: Click Profile > Settings on Github.com

Step 2: Click the Developer Settings Option

Step 3: Click Personal Access Tokens

Step 4: Click Generate New Token

Step 5: Add Note, Set an Expiration Date, Select Scopes, Click Generate Token

Step 7: Copy and paste your new access token to a safe place like a password manager!

For this step I’m not including a picture just to make sure I don’t accidentally expose my access token. But it should be pretty obvious. Simply copy the access token, then paste it into your password manager (Dashlane, 1Password, or whatever other password manager you use). If you don’t have a password manager, I suggest now is a good time to get one. Also, if you’re wondering: what the hell is a personal access token? It’s just a fancy password, so that’s why I advise you keep in your new password manager.

Step 8: Run crazy terminal commands to install Microsoft’s Git Credential Helper

I know, I thought there would be a better option than this. However, it makes sense that since Microsoft owns Github now, they’ve essentially manufactured a new problem that must be solved by using one of their products. So yeah, that’s the next step– open the terminal app on your Mac and run these commands to install Microsoft’s Git Credential Helper:

$ brew tap microsoft/git

$ brew install --cask git-credential-manager-core

UPDATE (February 26th, 2022): I’ve noticed recently that I personally can’t install the credential manager on my M1 MacBook Pro anymore using brew. I run into an error which looks something like:

Error: Failure while executing; `/usr/bin/sudo -E — /usr/bin/env LOGNAME=christopherpedersen USER=christopherpedersen USERNAME=christopherpedersen /usr/sbin/installer -pkg /opt/homebrew/Caskroom/git-credential-manager-core/2.0.632.34631/gcmcore-osx-2.0.632.34631.pkg -target / -allowUntrusted` exited with 1. Here’s the output:

installer: Package name is Git Credential Manager

installer: Installing at base path /

installer: The install failed. (The Installer encountered an error that caused the installation to fail. Contact the software manufacturer for assistance. An error occurred while running scripts from the package “gcmcore-osx-2.0.632.34631.pkg”.)

If you run into this issue, instead of using brew download the macOS pkg from Github. However, don’t download the latest version as I believe there’s a bug in the latest version. I would recommend downloading: gcmcore-osx-2.0.498.54650.pkg

After you download the pkg file, go to Finder > Downloads and then right-click on the pkg file and select “Open with Installer” and finish the installation.

Next, you’ll need to open up a new terminal window and enter:

$ git-credential-manager-core configure

A window should pop up at some point after running that command where you’ll be able to paste your new Personal Access Token which is the last step in setting up the Git Credential Manager.

Step 9: Last, run $ git push and paste your Personal Access Token into the Microsoft Git Credential Popup Window

This last step is actually pretty simply: You just run $ git push like you normally would from your project directory, but now a popup window from the Microsoft Git Credential Helper will appear where you can paste your new Personal Access Token. And that’s it, you’re back in business my friend 🙂

Bonus Step: Adding Your New Github Personal Access Token to Your Ubuntu Linux Server

Okay so this last step I came back and added one day later after writing the above steps for Mac. The steps above don’t really work server-side. Supposedly Microsoft’s Credential Manager supports Linux but I couldn’t get it to work for me. So here are my five magic terminal commands to add your new personal access token to your Ubuntu Linux Server:

$ ssh root@yoursupersweetserver.com

$ cd ~/yourproject

$ git config --global credential.helper 'cache --timeout=32850000'

$ git remote rm origin

$ git remote add origin https://yourGithubUsername:yOUrPeRsONALacceSsTOKeN@github.com/yourGithubUsername/yourproject.git